package com.jrx.anytxn.transaction.service.impl;

import com.jrx.anytxn.common.data.TxnRespCode;
import com.jrx.anytxn.common.exception.TxnBizException;
import com.jrx.anytxn.common.exception.TxnException;
import com.jrx.anytxn.common.function.ToDateFunction;
import com.jrx.anytxn.common.function.UncheckedBiConsumer;
import com.jrx.anytxn.common.function.UncheckedConsumer;
import com.jrx.anytxn.common.uitl.*;
import com.jrx.anytxn.customer.constant.CustomerConstant;
import com.jrx.anytxn.customer.dto.customer.CustomerRes;
import com.jrx.anytxn.customer.entity.CmCustomerRightInfo;
import com.jrx.anytxn.customer.entity.CmCustomerSecondLevel;
import com.jrx.anytxn.customer.service.ICustomerLimitInfoService;
import com.jrx.anytxn.customer.service.ICustomerRightService;
import com.jrx.anytxn.customer.service.ICustomerService;
import com.jrx.anytxn.param.dto.product.PaymentSequenceTableRes;
import com.jrx.anytxn.param.entity.*;
import com.jrx.anytxn.param.service.product.*;
import com.jrx.anytxn.param.service.system.ITransactionCodeTableService;
import com.jrx.anytxn.transaction.bean.*;
import com.jrx.anytxn.transaction.constant.TransBizConstant;
import com.jrx.anytxn.transaction.constant.TransErrMsgConstant;
import com.jrx.anytxn.transaction.entity.*;
import com.jrx.anytxn.transaction.handler.ILoanHandler;
import com.jrx.anytxn.transaction.mapper.ext.*;
import com.jrx.anytxn.transaction.service.*;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.ToIntFunction;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;

/**
 * @program: anytxn-cf-transaction
 * @description: 处理还款分配相关操作类
 * @author: xiaoyu.jing
 * @date: 2019-10-12 11:22
 **/
@Service
public class DealPaymentServiceImpl implements IDealPaymentService {

    private static final Logger logger = LoggerFactory.getLogger(DealPaymentServiceImpl.class);

    @Autowired
    private ITlPaymentAllocationLogService tlPaymentAllocationLogService;
    @Resource
    private ExtAmAccountMainInfoMapper extAmAccountMainInfoMapper;
    @Resource
    private ExtAmInterestAccrualInfoMapper extAmInterestAccrualInfoMapper;
    @Autowired
    private IAccountService accountService;
    @Resource
    private ExtTlPaymentAllocationLogMapper extTlPaymentAllocationLogMapper;
    @Resource
    private ExtTlGlInterfaceInfoMapper extTlGlInterfaceInfoMapper;
    @Resource
    private ExtAmRepaymentPlanInfoMapper extAmRepaymentPlanInfoMapper;
    @Resource
    private ExtAmDelinquencyInfoMapper extAmDelinquencyInfoMapper;
    @Autowired
    private IGlInterfaceService glInterfaceService;
    @Autowired
    private IFeeService feeService;
    @Autowired
    private IPaymentSequenceTableService paymentSequenceTableService;
    @Autowired
    private ICustomerLimitInfoService customerLimitInfoService;
    @Autowired
    private IRepaymentPlanService repaymentPlanService;
    @Autowired
    private IDebtCalculationService debtCalculationService;
    @Resource
    private ExtAmLoanAccountInfoMapper extAmLoanAccountInfoMapper;
    @Resource
    private ExtTlTransactionInfoMapper extTlTransactionInfoMapper;
    @Autowired
    private IDealDelqService delqService;
    @Autowired
    private IPrPenaltyRateTableService prPenaltyRateTableService;
    @Resource
    private ExtBtAccountingTransferListMapper extBtAccountingTransferListMapper;
    @Autowired
    private IBtAccountingTransferListService btAccountingTransferListService;
    @Resource
    private ExtAmLoanMainInfoMapper extAmLoanMainInfoMapper;
    @Autowired
    private IInterestTableService interestTableService;
    @Autowired
    private IProductTableService productTableService;
    @Autowired
    private IWaivePaymentLogService waivePaymentLogService;
    @Autowired
    private ICustomerService customerService;
    @Resource
    private ExtTlWaivePaymentLogMapper extTlWaivePaymentLogMapper;
    @Resource
    private ExtTlWaivePaymentMappingMapper extTlWaivePaymentMappingMapper;
    @Autowired
    private ITlTransactionInfoService tlTransactionInfoService;
    @Autowired
    private IPartnerLimitRecoveryService partnerLimitRecoveryService;
    @Autowired
    private ICustomerRightService customerRightService;
    @Resource
    private ExtAmWaiveRepaymentPlanInfoMapper extAmWaiveRepaymentPlanInfoMapper;
    @Autowired
    private ITlLoanClassifyService tlLoanClassifyService;
    @Resource
    private ExtTlLoanClassifyChangeMapper extTlLoanClassifyChangeMapper;
    @Autowired
    private ILoanHandler loanHandlerImpl;
    @Resource
    private ExtTlLoanStagingMappingMapper extTlLoanStagingMappingMapper;
    @Autowired
    private IDelqTableService delqTableService;
    @Autowired
    private ITransactionCodeTableService transactionCodeTableService;

    /**
     * 逾期还款还款分配
     *
     * @param paymentLog       还款流水
     * @param loanMainInfoList 贷款信息列表
     * @param bussinessDate    业务日期
     * @throws TxnException 自定义异常
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void dealOverPay(TlPaymentLog paymentLog, List<AmLoanMainInfo> loanMainInfoList, Date bussinessDate) throws TxnException {
        PaymentSaveBean saveBean = this.dealOverPayNotSave(paymentLog, loanMainInfoList, bussinessDate);
        this.saveAllAccts(saveBean);
    }

    /**
     * 逾期还款还款分配
     *
     * @param paymentLog       还款流水
     * @param loanMainInfoList 贷款信息列表
     * @param bussinessDate    业务日期
     * @throws TxnException 自定义异常
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public PaymentSaveBean dealOverPayNotSave(TlPaymentLog paymentLog, List<AmLoanMainInfo> loanMainInfoList, Date bussinessDate) throws TxnException {
        logger.info("逾期还款还款分配开始 paymentLog:{}", BeanUtils.toMap(paymentLog));
        // 1.在还款分配历史表 tl_payment_allocation_log生成08还款交易账户
        TlPaymentAllocationLog totalPaymentLog = tlPaymentAllocationLogService.buildTlPaymentAllocationLog(paymentLog, loanMainInfoList.get(0), bussinessDate);
        // 2.生成还款交易流水
        TlTransactionInfo tlTransactionInfo = tlTransactionInfoService.buildTlTransactionInfo(totalPaymentLog);
        //总还款金额
        BigDecimal paymentAmt = totalPaymentLog.getPostingAmount();
        List<PaymentSaveBean> saveBeanList = new ArrayList<>();
        //循环处理每个订单
        for (AmLoanMainInfo loanMainInfo : loanMainInfoList) {
            logger.info("逾期还款循环分配订单开始 loanMainInfo:{}", BeanUtils.toMap(loanMainInfo));
            PaymentSaveBean saveBean = new PaymentSaveBean();
            saveBean.setSavaPaymentLog(totalPaymentLog);
            //获取订单账户信息
            AmLoanAccountInfo loanAccountInfo = Optional.ofNullable(extAmLoanAccountInfoMapper.selectByLoanId(loanMainInfo.getLoanId(),
                    loanMainInfo.getCustomerId(), loanMainInfo.getTenantId()))
                    .orElseThrow(() -> {
                        logger.error("订单交易账户为空，loanId:{}", loanMainInfo.getLoanId());
                        return new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
                    });
            logger.info("订单账户信息loanAccountInfo:{}", BeanUtils.toMap(loanAccountInfo));


            // 2.获取还款需要处理的还款计划
            this.getNeedDealPlan(saveBean, loanMainInfo, Collections.singletonList(TransBizConstant.LOAN_PLAN_STATUS_2));

            // 3.获取逾期本金，利息，罚息，罚息复利,费用交易账户
            this.getAlreadyCastAcct(saveBean, loanMainInfo, Arrays.asList(TransBizConstant.ACCOUNT_STATUS_1, TransBizConstant.ACCOUNT_STATUS_2));

            // 4.罚息,罚息复利累计账户抛帐，获取生成的罚息,罚息复利交易账户,并关闭累计
            this.castAccuAcct(saveBean, loanMainInfo, loanAccountInfo, Arrays.asList(TransBizConstant.INTEREST_ACCRUAL_TYPE_10
                    , TransBizConstant.INTEREST_ACCRUAL_TYPE_20), bussinessDate);

            // 5.计算欠款
            DebtBean debtBean = debtCalculationService.calculationDebt(saveBean.getNeedDealAcctList());

            // 6.将这些交易账户按照分配顺序真正分配，生成09交易账户，并关闭交易账户
            paymentAmt = this.repaymentAllocation(saveBean, loanMainInfo, loanAccountInfo, debtBean, paymentAmt, bussinessDate);

            // 7. 生成09对应的gl流水
            List<TlGlInterfaceInfo> saveGlList = glInterfaceService.buildTlGlInterfaceInfoList(saveBean.getPaymentLogSaveList(), loanMainInfo, loanAccountInfo);
            saveBean.addSaveGlList(saveGlList);

            // 8.处理还款计划
            this.dealPlan(saveBean, bussinessDate);

            // 9.处理订单
            this.dealOrder(saveBean, loanMainInfo, loanAccountInfo);

            // 10.处理延滞
            DelqParamBean delqParamBean = delqService.buildDqlqAccountBean(loanMainInfo.getTenantId(), loanMainInfo.getDelqTableId());
            this.dealDelq(saveBean, loanMainInfo, loanAccountInfo, delqParamBean, bussinessDate);

            // 11 判断是否记录五级分类变动
            if (TransBizConstant.LOAN_STATUS_7.equals(saveBean.getUpdateLoan().getAccountStatus()) ||
                    TransBizConstant.LOAN_STATUS_8.equals(saveBean.getUpdateLoan().getAccountStatus())) {
                String alterClass = saveBean.getUpdateLoan().getLoanClassify();
                if (StringUtils.isBlank(alterClass)) {
                    //如果为空表示五级分类不可逆，只是保存一条流水
                    alterClass = loanAccountInfo.getLoanClassify();
                }
                TlLoanClassifyChange tlLoanClassifyChange = tlLoanClassifyService.buildLoanClassifyChange(loanAccountInfo, bussinessDate, loanAccountInfo.getRemainPrincipal(), alterClass);
                saveBean.getSaveClassifyList().add(tlLoanClassifyChange);
            }

            saveBeanList.add(saveBean);
        }

        // 11.更新客户额度
        this.updateCustLmt(saveBeanList, loanMainInfoList);

        // 12. 更新合作方额度
        this.updatePartnerLimit(saveBeanList, loanMainInfoList, paymentLog.getSeqNo());

        // 12.保存所信息
        return this.dealAllAccts(saveBeanList, tlTransactionInfo);
    }

    /**
     * 逾期还款计算 试算计算分配金额
     *
     * @param loanMainInfoList 贷款订单列表
     * @param loanAccountInfos 贷款订单账户列表
     * @param businessDate     业务日期
     * @param repayAmt         还款金额
     * @throws TxnException 自定义业务异常
     */
    @Override
    public List<TlPaymentAllocationLog> calculationOverPay(List<AmLoanMainInfo> loanMainInfoList, List<AmLoanAccountInfo> loanAccountInfos, Date businessDate, BigDecimal repayAmt) throws TxnBizException {

        List<TlPaymentAllocationLog> paymentAllocationLogs = new ArrayList<>();
        /**
         * 08总账
         */
        TlPaymentAllocationLog tlPaymentAllocationLog = new TlPaymentAllocationLog();
        tlPaymentAllocationLog.setPostingAmount(repayAmt);

        BigDecimal paymentAmt = tlPaymentAllocationLog.getPostingAmount();

        //按订单时间先后顺序排序
        loanMainInfoList = loanMainInfoList.stream().sorted(Comparator.comparing(AmLoanMainInfo::getPostingDate)).collect(Collectors.toList());

        Map<String, AmLoanAccountInfo> loanMainInfoMap = loanAccountInfos.stream().collect(Collectors.toMap(AmLoanAccountInfo::getLoanId, Function.identity()));
        //循环处理每个订单
        for (AmLoanMainInfo loanMainInfo : loanMainInfoList) {
            logger.info("逾期还款循环分配订单开始 loanMainInfo:{}", BeanUtils.toMap(loanMainInfo));
            //获取订单账户信息
            AmLoanAccountInfo loanAccountInfo = Optional.ofNullable(loanMainInfoMap.get(loanMainInfo.getLoanId()))
                    .orElseThrow(() -> {
                        logger.error("订单交易账户为空，loanId:{}", loanMainInfo.getLoanId());
                        return new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
                    });
            logger.info("订单账户信息loanAccountInfo:{}", BeanUtils.toMap(loanAccountInfo));
            PaymentSaveBean saveBean = new PaymentSaveBean();
            saveBean.setSavaPaymentLog(tlPaymentAllocationLog);

            // 2.获取还款需要处理的还款计划
            this.getNeedDealPlan(saveBean, loanMainInfo, Collections.singletonList(TransBizConstant.LOAN_PLAN_STATUS_2));

            // 3.获取逾期本金，利息，罚息，罚息复利,费用交易账户
            this.getAlreadyCastAcct(saveBean, loanMainInfo, Arrays.asList(TransBizConstant.ACCOUNT_STATUS_1, TransBizConstant.ACCOUNT_STATUS_2));

            // 4.罚息,罚息复利累计账户抛帐，获取生成的罚息,罚息复利交易账户,并关闭累计
            this.castAccuAcct(saveBean, loanMainInfo, loanAccountInfo, Arrays.asList(TransBizConstant.INTEREST_ACCRUAL_TYPE_10
                    , TransBizConstant.INTEREST_ACCRUAL_TYPE_20, TransBizConstant.INTEREST_ACCRUAL_TYPE_66), businessDate);

            // 5.计算欠款
            DebtBean debtBean = debtCalculationService.calculationDebt(saveBean.getNeedDealAcctList());

            // 6.将这些交易账户按照分配顺序真正分配，生成09交易账户，并关闭交易账户
            paymentAmt = this.repaymentAllocation(saveBean, loanMainInfo, loanAccountInfo, debtBean, paymentAmt, businessDate);

            paymentAllocationLogs.addAll(saveBean.getPaymentLogSaveList());
        }
        return paymentAllocationLogs;
    }

    /**
     * 提前结清还款分配
     *
     * @param paymentLog       还款流水
     * @param loanMainInfoList 贷款信息列表
     * @param bussinessDate    业务日期
     * @throws TxnException 自定义异常
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void dealPayOff(TlPaymentLog paymentLog, List<AmLoanMainInfo> loanMainInfoList, Date bussinessDate) throws TxnException {
        PaymentSaveBean saveBean = this.dealPayOffNotSave(paymentLog, loanMainInfoList, bussinessDate);
        this.saveAllAccts(saveBean);
    }

    /**
     * 提前结清还款分配
     *
     * @param paymentLog       还款流水
     * @param loanMainInfoList 贷款信息列表
     * @param bussinessDate    业务日期
     * @throws TxnException 自定义异常
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public PaymentSaveBean dealPayOffNotSave(TlPaymentLog paymentLog, List<AmLoanMainInfo> loanMainInfoList, Date bussinessDate) throws TxnException {
        logger.info("提前结清还款分配开始 paymentLog:{}", BeanUtils.toMap(paymentLog));
        // 1.在还款分配历史表 tl_payment_allocation_log生成08还款交易账户
        TlPaymentAllocationLog totalPaymentLog = tlPaymentAllocationLogService.buildTlPaymentAllocationLog(paymentLog, loanMainInfoList.get(0), bussinessDate);

        // 2.生成还款交易流水
        TlTransactionInfo tlTransactionInfo = tlTransactionInfoService.buildTlTransactionInfo(totalPaymentLog);

        BigDecimal paymentAmt = totalPaymentLog.getPostingAmount();
        List<PaymentSaveBean> saveBeanList = new ArrayList<>();
        //循环处理每个订单
        for (AmLoanMainInfo loanMainInfo : loanMainInfoList) {
            logger.info("提前结清循环分配订单开始 loanMainInfo:{}", BeanUtils.toMap(loanMainInfo));
            PaymentSaveBean saveBean = new PaymentSaveBean();
            saveBean.setSavaPaymentLog(totalPaymentLog);

            //获取订单账户信息
            AmLoanAccountInfo loanAccountInfo = Optional.ofNullable(extAmLoanAccountInfoMapper.selectByLoanId(loanMainInfo.getLoanId(),
                    loanMainInfo.getCustomerId(), loanMainInfo.getTenantId()))
                    .orElseThrow(() -> {
                        logger.error("订单交易账户为空，loanId:{}", loanMainInfo.getLoanId());
                        return new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
                    });
            logger.info("订单账户信息loanAccountInfo:{}", BeanUtils.toMap(loanAccountInfo));


            // 2.获取还款需要处理的还款计划
            this.getNeedDealPlan(saveBean, loanMainInfo, Arrays.asList(TransBizConstant.LOAN_PLAN_STATUS_0
                    , TransBizConstant.LOAN_PLAN_STATUS_1, TransBizConstant.LOAN_PLAN_STATUS_2));

            // 3.获取逾期本金，利息，罚息，罚息复利,费用交易账户
            this.getAlreadyCastAcct(saveBean, loanMainInfo
                    , Arrays.asList(TransBizConstant.ACCOUNT_STATUS_1, TransBizConstant.ACCOUNT_STATUS_2));

            // 4.利息,罚息,罚息复利累计账户抛帐，获取生成的利息,罚息,罚息复利交易账户,并关闭累计
            this.castAccuAcct(saveBean, loanMainInfo, loanAccountInfo, Arrays.asList(TransBizConstant.INTEREST_ACCRUAL_TYPE_66,
                    TransBizConstant.INTEREST_ACCRUAL_TYPE_10, TransBizConstant.INTEREST_ACCRUAL_TYPE_20), bussinessDate);

            DelqParamBean delqParamBean = delqService.buildDqlqAccountBean(loanMainInfo.getTenantId(), loanMainInfo.getDelqTableId());
            // 5.未抛帐的期数本金抛帐，获取生成的本金交易账户
            this.castNonCastPrinAcct(saveBean, loanMainInfo, loanAccountInfo, delqParamBean, bussinessDate);

            // 6.提前结清手续费抛帐获取生成的费用交易账户
            feeService.castPayOffFeeAndIntrAcct(saveBean, loanMainInfo, loanAccountInfo, delqParamBean, bussinessDate);

            // 7.计算欠款
            DebtBean debtBean = debtCalculationService.calculationDebt(saveBean.getNeedDealAcctList());

            // 8.将这些交易账户按照分配顺序真正分配，生成09交易账户，关闭交易账户
            paymentAmt = this.repaymentAllocation(saveBean, loanMainInfo, loanAccountInfo, debtBean, paymentAmt, bussinessDate);

            // 9.生成09对应的gl流水
            List<TlGlInterfaceInfo> saveGlList = glInterfaceService.buildTlGlInterfaceInfoList(saveBean.getPaymentLogSaveList(), loanMainInfo, loanAccountInfo);
            saveBean.addSaveGlList(saveGlList);

            // 10.处理还款计划
            this.dealPlan(saveBean, bussinessDate);

            // 11.处理订单
            this.dealOrder(saveBean, loanMainInfo, loanAccountInfo);

            // 12.处理延滞
            this.dealDelq(saveBean, loanMainInfo, loanAccountInfo, delqParamBean, bussinessDate);

            // 13.贴息产品生成贴息还款流水
            PrProductTable prProductTable = productTableService.findProByProIdAndOrgId(loanMainInfo.getProductId(), loanMainInfo.getOrganizationId(), loanMainInfo.getTenantId());
            this.dealWaive(saveBean, loanMainInfo, loanAccountInfo, prProductTable, bussinessDate);

            // 14.结清订单保存五级分类
            if (TransBizConstant.LOAN_STATUS_7.equals(saveBean.getUpdateLoan().getAccountStatus()) ||
                    TransBizConstant.LOAN_STATUS_8.equals(saveBean.getUpdateLoan().getAccountStatus())) {
                String alterClass = saveBean.getUpdateLoan().getLoanClassify();
                if (StringUtils.isBlank(alterClass)) {
                    //如果为空表示五级分类不可逆，只是保存一条流水
                    alterClass = loanAccountInfo.getLoanClassify();
                }
                TlLoanClassifyChange tlLoanClassifyChange = tlLoanClassifyService.buildLoanClassifyChange(loanAccountInfo, bussinessDate, loanAccountInfo.getRemainPrincipal(), alterClass);
                saveBean.getSaveClassifyList().add(tlLoanClassifyChange);
            }
            saveBeanList.add(saveBean);
        }

        // 15.更新客户额度
        this.updateCustLmt(saveBeanList, loanMainInfoList);

        //16. 恢复合作方额度
        this.updatePartnerLimit(saveBeanList, loanMainInfoList, paymentLog.getSeqNo());

        // 16.保存所信息
        return this.dealAllAccts(saveBeanList, tlTransactionInfo);
    }

    /**
     * 提前还款还款分配
     *
     * @param paymentLog    还款流水
     * @param loanMainInfo  贷款信息列表
     * @param bussinessDate 业务日期
     * @throws TxnException 自定义异常
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void dealPrePay(TlPaymentLog paymentLog, AmLoanMainInfo loanMainInfo, Date bussinessDate) throws TxnException {
        PaymentSaveBean paymentSaveBean = this.dealPrePayNotSave(paymentLog, loanMainInfo, bussinessDate);
        this.saveAllAccts(paymentSaveBean);
    }

    @Override
    public PaymentSaveBean dealPrePayNotSave(TlPaymentLog paymentLog, AmLoanMainInfo loanMainInfo, Date bussinessDate) throws TxnException {
        //获取订单账户信息
        AmLoanAccountInfo loanAccountInfo = Optional.ofNullable(extAmLoanAccountInfoMapper.selectByLoanId(loanMainInfo.getLoanId(),
                loanMainInfo.getCustomerId(), loanMainInfo.getTenantId()))
                .orElseThrow(() -> {
                    logger.error("订单交易账户为空，loanId:{}", loanMainInfo.getLoanId());
                    return new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
                });
        logger.info("订单账户信息loanAccountInfo:{}", BeanUtils.toMap(loanAccountInfo));
        return this.dealPrePayNotSave(paymentLog, loanMainInfo, loanAccountInfo, bussinessDate);
    }


    private PaymentSaveBean dealPrePayNotSave(TlPaymentLog paymentLog, AmLoanMainInfo loanMainInfo, AmLoanAccountInfo loanAccountInfo, Date bussinessDate) throws TxnException {
        logger.info("提前还款还款分配开始 paymentLog:{},loanMainInfo:{}", BeanUtils.toMap(paymentLog), BeanUtils.toMap(loanMainInfo));
        PaymentSaveBean saveBean = new PaymentSaveBean();

        // 1.在还款分配历史表 tl_payment_allocation_log生成08还款交易账户
        TlPaymentAllocationLog totalPaymentLog = tlPaymentAllocationLogService.buildTlPaymentAllocationLog(paymentLog,
                loanMainInfo, bussinessDate);

        // 2.生成还款交易流水
        TlTransactionInfo tlTransactionInfo = tlTransactionInfoService.buildTlTransactionInfo(totalPaymentLog);

        BigDecimal paymentAmt = totalPaymentLog.getPostingAmount();
        saveBean.setSavaPaymentLog(totalPaymentLog);

        // 2.获取还款需要处理的还款计划
        this.getNeedDealPlan(saveBean, loanMainInfo, Arrays.asList(TransBizConstant.LOAN_PLAN_STATUS_0, TransBizConstant.LOAN_PLAN_STATUS_1));

        // 3.获取正常本金，利息，罚息，罚息复利,费用交易账户
        this.getAlreadyCastAcct(saveBean, loanMainInfo
                , Collections.singletonList((TransBizConstant.ACCOUNT_STATUS_1)));

        // 4.利息累计账户抛帐，获取生成的利息交易账户,并关闭累计
        this.castAccuAcct(saveBean, loanMainInfo, loanAccountInfo,
                Collections.singletonList(TransBizConstant.INTEREST_ACCRUAL_TYPE_66), bussinessDate);

        // 5.提前还款手续费抛帐获取生成的费用交易账户
        String liabilityFlag = saveBean.getPlanInfoMap().values().stream().findFirst().get().getLiabilityFlag();
        CastAccountTransactionBean castAccountTransactionBean = feeService.getPrePayFeeAcct(loanMainInfo, loanAccountInfo, bussinessDate, liabilityFlag);
        Optional.ofNullable(castAccountTransactionBean).ifPresent(bean -> {
            saveBean.addSaveAcct(bean.getAmAccountMainInfo());
            saveBean.addTransactionInfo(bean.getTlTransactionInfo());
            saveBean.addSaveGlList(bean.getTlGlInterfaceInfoList());
        });

        // 6.计算欠款
        DebtBean debtBean = debtCalculationService.calculationDebt(saveBean.getNeedDealAcctList());

        DelqParamBean delqParamBean = delqService.buildDqlqAccountBean(loanMainInfo.getTenantId(), loanMainInfo.getDelqTableId());
        // 7.根据还款金额和欠款金额，调整本金交易账户
        this.adjustPrinAcct(saveBean, loanMainInfo, loanAccountInfo, debtBean, delqParamBean, bussinessDate);

        // 8.将这些交易账户按照分配顺序真正分配，生成09交易账户，并关闭交易账户
        this.repaymentAllocation(saveBean, loanMainInfo, loanAccountInfo, debtBean, paymentAmt, bussinessDate);

        // 9. 生成09对应的gl流水
        List<TlGlInterfaceInfo> saveGlList = glInterfaceService.buildTlGlInterfaceInfoList(saveBean.getPaymentLogSaveList(), loanMainInfo, loanAccountInfo);
        saveBean.addSaveGlList(saveGlList);

        PrProductTable prProductTable = productTableService.findProByProIdAndOrgId(loanMainInfo.getProductId(), loanMainInfo.getOrganizationId(), loanMainInfo.getTenantId());
        // 10.处理提前还款还款计划
        this.dealPrePayPlan(saveBean, loanMainInfo, loanAccountInfo, debtBean, prProductTable, bussinessDate);

        // 11.根据重新生成的还款计划，对当期本金，利息累计,分期费用重新抛帐
        this.reCastAcct(saveBean, loanMainInfo, loanAccountInfo, bussinessDate);

        // 12.更新订单
        this.dealOrder(saveBean, loanMainInfo, loanAccountInfo);

        // 13.更新客户额度
        if (!TransBizConstant.REPAYMENT_TYPE_STAGING.equals(paymentLog.getPaymentType())) {
            this.updateCustLmt(saveBean, loanMainInfo);
        }

        // 14.恢复合作方额度
        if (!TransBizConstant.REPAYMENT_TYPE_STAGING.equals(paymentLog.getPaymentType())) {
            this.updatePartnerLimit(saveBean, loanMainInfo, paymentLog.getSeqNo());
        }

        // 15.贴息产品生成贴息还款流水
        this.dealWaive(saveBean, loanMainInfo, loanAccountInfo, prProductTable, bussinessDate);

        // 16 判断是否记录五级分类变动
        if (TransBizConstant.LOAN_STATUS_7.equals(saveBean.getUpdateLoan().getAccountStatus()) ||
                TransBizConstant.LOAN_STATUS_8.equals(saveBean.getUpdateLoan().getAccountStatus())) {
            String alterClass = saveBean.getUpdateLoan().getLoanClassify();
            if (StringUtils.isBlank(alterClass)) {
                //如果为空表示五级分类不可逆，只是保存一条流水
                alterClass = loanAccountInfo.getLoanClassify();
            }
            TlLoanClassifyChange tlLoanClassifyChange = tlLoanClassifyService.buildLoanClassifyChange(loanAccountInfo, bussinessDate, loanAccountInfo.getRemainPrincipal(), alterClass);
            saveBean.getSaveClassifyList().add(tlLoanClassifyChange);
        }

        List<TlTransactionInfo> transactionInfos = saveBean.getTransactionInfoList();
        transactionInfos.add(tlTransactionInfo);

        // 13.保存所信息
        return saveBean;
    }

    @Override
    public List<TlPaymentAllocationLog> calculationPrePay(AmLoanAccountInfo loanAccountInfo, AmLoanMainInfo loanMainInfo,
                                                          Date businessDate, BigDecimal repayAmt) throws TxnException {
        PaymentSaveBean saveBean = new PaymentSaveBean();
        /**
         * 08总账
         */
        TlPaymentAllocationLog tlPaymentAllocationLog = new TlPaymentAllocationLog();
        tlPaymentAllocationLog.setPostingAmount(repayAmt);

        saveBean.setSavaPaymentLog(tlPaymentAllocationLog);
        // 2.获取还款需要处理的还款计划
        this.getNeedDealPlan(saveBean, loanMainInfo, Arrays.asList(TransBizConstant.LOAN_PLAN_STATUS_0, TransBizConstant.LOAN_PLAN_STATUS_1));

        // 3.获取逾期本金，利息，罚息，罚息复利,费用交易账户
        this.getAlreadyCastAcct(saveBean, loanMainInfo
                , Collections.singletonList((TransBizConstant.ACCOUNT_STATUS_1)));

        // 4.利息累计账户抛帐，获取生成的利息交易账户,并关闭累计
        this.castAccuAcct(saveBean, loanMainInfo, loanAccountInfo,
                Collections.singletonList(TransBizConstant.INTEREST_ACCRUAL_TYPE_66), businessDate);

        // 5.提前还款手续费抛帐获取生成的费用交易账户
        String liabilityFlag = saveBean.getPlanInfoMap().values().stream().findFirst().get().getLiabilityFlag();
        CastAccountTransactionBean castAccountTransactionBean = feeService.getPrePayFeeAcct(loanMainInfo, loanAccountInfo, businessDate, liabilityFlag);
        Optional.ofNullable(castAccountTransactionBean).ifPresent(bean -> {
            saveBean.addSaveAcct(bean.getAmAccountMainInfo());
            saveBean.addTransactionInfo(bean.getTlTransactionInfo());
            saveBean.addSaveGlList(bean.getTlGlInterfaceInfoList());
        });

        // 6.计算欠款
        DebtBean debtBean = debtCalculationService.calculationDebt(saveBean.getNeedDealAcctList());

        DelqParamBean delqParamBean = delqService.buildDqlqAccountBean(loanMainInfo.getTenantId(), loanMainInfo.getDelqTableId());
        // 7.根据还款金额和欠款金额，调整本金交易账户
        this.adjustPrinAcct(saveBean, loanMainInfo, loanAccountInfo, debtBean, delqParamBean, businessDate);

        // 8.将这些交易账户按照分配顺序真正分配，生成09交易账户，
        return this.preRepaymentAllocation(saveBean, loanMainInfo, debtBean, repayAmt);
    }

    /**
     * 正常还款
     *
     * @param paymentLog    还款流水
     * @param loanMainInfos 贷款信息列表
     * @param bussinessDate 业务日期
     * @throws TxnException 自定义异常
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void dealPay(TlPaymentLog paymentLog, List<AmLoanMainInfo> loanMainInfos, Date bussinessDate) throws TxnException {
        PaymentSaveBean paymentSaveBean = this.dealPayNotSave(paymentLog, loanMainInfos, bussinessDate);
        this.saveAllAccts(paymentSaveBean);
    }

    /**
     * 正常还款
     *
     * @param paymentLog    还款流水
     * @param loanMainInfos 贷款信息列表
     * @param bussinessDate 业务日期
     * @throws TxnException 自定义异常
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public PaymentSaveBean dealPayNotSave(TlPaymentLog paymentLog, List<AmLoanMainInfo> loanMainInfos, Date bussinessDate) throws TxnException {
        /* 还款额 */
        BigDecimal paymentAmt = paymentLog.getAmount();
        /* 还款分配入库bean */
        List<PaymentSaveBean> saveBeanList = new ArrayList<>();

        // 1.在还款分配历史表 tl_payment_allocation_log生成08还款交易账户
        TlPaymentAllocationLog totalPaymentLog = tlPaymentAllocationLogService.buildTlPaymentAllocationLog(paymentLog,
                loanMainInfos.get(0), bussinessDate);

        // 2.生成还款交易流水
        TlTransactionInfo tlTransactionInfo = tlTransactionInfoService.buildTlTransactionInfo(totalPaymentLog);

        for (AmLoanMainInfo loanMainInfo : loanMainInfos) {
            PaymentSaveBean saveBean = new PaymentSaveBean();
            saveBean.setSavaPaymentLog(totalPaymentLog);

            //获取订单账户信息
            AmLoanAccountInfo loanAccountInfo = Optional.ofNullable(extAmLoanAccountInfoMapper.selectByLoanId(loanMainInfo.getLoanId(),
                    loanMainInfo.getCustomerId(), loanMainInfo.getTenantId()))
                    .orElseThrow(() -> {
                        logger.error("订单交易账户为空，loanId:{}", loanMainInfo.getLoanId());
                        return new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
                    });
            logger.info("订单账户信息loanAccountInfo:{}", BeanUtils.toMap(loanAccountInfo));

            // 2.获取还款需要处理的还款计划
            this.getNeedDealPlan(saveBean, loanMainInfo, Arrays.asList(TransBizConstant.LOAN_PLAN_STATUS_1,
                    TransBizConstant.LOAN_PLAN_STATUS_2));

            // 3.过滤还款计划  核销的订单不需要过滤(核销需要全部结清)
            if (!TransBizConstant.YES_FLAG.equals(loanAccountInfo.getChargeoffFlag())) {
                this.filterPlanMap(saveBean, loanMainInfo, bussinessDate);
            }

            // 4.获取逾期本金，利息，罚息，罚息复利,费用交易账户
            this.getAlreadyCastAcct(saveBean, loanMainInfo
                    , Arrays.asList(TransBizConstant.ACCOUNT_STATUS_1, TransBizConstant.ACCOUNT_STATUS_2));

            // 5.利息，罚息,罚息复利累计账户抛帐，获取生成的罚息,罚息复利交易账户,并关闭累计
            this.castAccuAcct(saveBean, loanMainInfo, loanAccountInfo, Arrays.asList(TransBizConstant.INTEREST_ACCRUAL_TYPE_66, TransBizConstant.INTEREST_ACCRUAL_TYPE_10,
                    TransBizConstant.INTEREST_ACCRUAL_TYPE_20), bussinessDate);

            // 6.计算欠款
            DebtBean debtBean = debtCalculationService.calculationDebt(saveBean.getNeedDealAcctList());

            // 7.将这些交易账户按照分配顺序真正分配，生成09交易账户 并关闭交易账户
            paymentAmt = this.repaymentAllocation(saveBean, loanMainInfo, loanAccountInfo, debtBean, paymentAmt, bussinessDate);

            // 8. 生成09对应的gl流水 添加贷款账户主要是交易码转换用
            List<TlGlInterfaceInfo> saveGlList = glInterfaceService.buildTlGlInterfaceInfoList(saveBean.getPaymentLogSaveList(), loanMainInfo, loanAccountInfo);
            saveBean.addSaveGlList(saveGlList);

            // 9.处理还款计划
            this.dealPlan(saveBean, bussinessDate);

            // 11.处理订单
            this.dealOrder(saveBean, loanMainInfo, loanAccountInfo);

            // 10.处理延滞
            DelqParamBean delqParamBean = delqService.buildDqlqAccountBean(loanMainInfo.getTenantId(), loanMainInfo.getDelqTableId());
            this.dealDelq(saveBean, loanMainInfo, loanAccountInfo, delqParamBean, bussinessDate);

            // 11 判断是否记录五级分类变动
            if (TransBizConstant.LOAN_STATUS_7.equals(saveBean.getUpdateLoan().getAccountStatus()) ||
                    TransBizConstant.LOAN_STATUS_8.equals(saveBean.getUpdateLoan().getAccountStatus())) {
                String alterClass = saveBean.getUpdateLoan().getLoanClassify();
                if (StringUtils.isBlank(alterClass)) {
                    //如果为空表示五级分类不可逆，只是保存一条流水
                    alterClass = loanAccountInfo.getLoanClassify();
                }
                TlLoanClassifyChange tlLoanClassifyChange = tlLoanClassifyService.buildLoanClassifyChange(loanAccountInfo, bussinessDate, loanAccountInfo.getRemainPrincipal(), alterClass);
                saveBean.getSaveClassifyList().add(tlLoanClassifyChange);
            }

            saveBeanList.add(saveBean);
        }

        // 12.更新客户额度
        this.updateCustLmt(saveBeanList, loanMainInfos);

        //更新合作方额度
        this.updatePartnerLimit(saveBeanList, loanMainInfos, paymentLog.getSeqNo());

        // 13.保存所信息
        return this.dealAllAccts(saveBeanList, tlTransactionInfo);
    }

    /**
     * 账单逾期还款分配
     *
     * @param paymentLog       还款流水
     * @param loanMainInfoList 贷款信息列表
     * @param bussinessDate    业务日期
     * @throws TxnException 自定义异常
     */
    private PaymentSaveBean dealBillOverDuePayNotSave(TlPaymentLog paymentLog, List<AmLoanMainInfo> loanMainInfoList, Date bussinessDate) throws TxnException {
        logger.info("逾期还款还款分配开始 paymentLog:{}", BeanUtils.toMap(paymentLog));
        // 1.在还款分配历史表 tl_payment_allocation_log生成08还款交易账户
        TlPaymentAllocationLog totalPaymentLog = tlPaymentAllocationLogService.buildTlPaymentAllocationLog(paymentLog, loanMainInfoList.get(0), bussinessDate);
        // 2.生成还款交易流水
        TlTransactionInfo tlTransactionInfo = tlTransactionInfoService.buildTlTransactionInfo(totalPaymentLog);

        //总还款金额
        BigDecimal paymentAmt = totalPaymentLog.getPostingAmount();
        BigDecimal totalDebt = BigDecimal.ZERO;
        List<AmLoanAccountInfo> loanAccountInfoList = new ArrayList<>();
        List<PaymentSaveBean> saveBeanList = new ArrayList<>();
        PaymentSaveBean saveBean = new PaymentSaveBean();
        saveBean.setSavaPaymentLog(totalPaymentLog);

        //循环处理每个订单的分配前准备
        for (AmLoanMainInfo loanMainInfo : loanMainInfoList) {
            logger.info("逾期还款循环分配订单开始 loanMainInfo:{}", BeanUtils.toMap(loanMainInfo));
            //获取订单账户信息
            AmLoanAccountInfo loanAccountInfo = Optional.ofNullable(extAmLoanAccountInfoMapper.selectByLoanId(loanMainInfo.getLoanId(),
                    loanMainInfo.getCustomerId(), loanMainInfo.getTenantId()))
                    .orElseThrow(() -> {
                        logger.error("订单交易账户为空，loanId:{}", loanMainInfo.getLoanId());
                        return new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
                    });
            logger.info("订单账户信息loanAccountInfo:{}", BeanUtils.toMap(loanAccountInfo));
            loanAccountInfoList.add(loanAccountInfo);

            // 2.获取还款需要处理的还款计划
            this.getNeedDealPlan(saveBean, loanMainInfo, Collections.singletonList(TransBizConstant.LOAN_PLAN_STATUS_2));

            // 3.获取逾期本金，利息，罚息，罚息复利,费用交易账户
            this.getAlreadyCastAcct(saveBean, loanMainInfo, Collections.singletonList(TransBizConstant.ACCOUNT_STATUS_2));

            // 4.罚息,罚息复利累计账户抛帐，获取生成的罚息,罚息复利交易账户,并关闭累计
            this.castAccuAcct(saveBean, loanMainInfo, loanAccountInfo, Arrays.asList(TransBizConstant.INTEREST_ACCRUAL_TYPE_10
                    , TransBizConstant.INTEREST_ACCRUAL_TYPE_20), bussinessDate);
        }

        // 5.计算欠款
        DebtBean debtBean = debtCalculationService.calculationDebt(saveBean.getNeedDealAcctList());
        totalDebt = debtBean.getTotalAmount();

        // 6.将这些交易账户按照分配顺序真正分配，生成09交易账户，并关闭交易账户
        this.repaymentBillAllocation(saveBean, loanMainInfoList, loanAccountInfoList, totalDebt, paymentAmt, bussinessDate);
        // 7. 生成09对应的gl流水
        List<TlGlInterfaceInfo> saveGlList = glInterfaceService.buildTlGlInterfaceInfoList(saveBean.getPaymentLogSaveList(), loanMainInfoList, loanAccountInfoList);
        saveBean.addSaveGlList(saveGlList);

        for (AmLoanMainInfo loanMainInfo : loanMainInfoList) {
            String loanId = loanMainInfo.getLoanId();
            AmLoanAccountInfo loanAccountInfo = loanAccountInfoList.stream().filter(loan -> loan.getLoanId().equals(loanId)).findFirst().get();

            //生成有后续每个订单处理的saveBean
            PaymentSaveBean saveBeanOrder = this.afterBillAllocation(saveBean, loanMainInfo);

            // 8.处理还款计划
            this.dealPlan(saveBeanOrder, bussinessDate);

            // 9.处理订单
            this.dealOrder(saveBeanOrder, loanMainInfo, loanAccountInfo);

            // 10.处理延滞
            DelqParamBean delqParamBean = delqService.buildDqlqAccountBean(loanMainInfo.getTenantId(), loanMainInfo.getDelqTableId());
            this.dealDelq(saveBeanOrder, loanMainInfo, loanAccountInfo, delqParamBean, bussinessDate);

            // 11 判断是否记录五级分类变动
            if (null != saveBeanOrder.getUpdateLoan() && (TransBizConstant.LOAN_STATUS_7.equals(saveBeanOrder.getUpdateLoan().getAccountStatus()) ||
                    TransBizConstant.LOAN_STATUS_8.equals(saveBeanOrder.getUpdateLoan().getAccountStatus()))) {
                String alterClass = saveBeanOrder.getUpdateLoan().getLoanClassify();
                if (StringUtils.isBlank(alterClass)) {
                    //如果为空表示五级分类不可逆，只是保存一条流水
                    alterClass = loanAccountInfo.getLoanClassify();
                }
                TlLoanClassifyChange tlLoanClassifyChange = tlLoanClassifyService.buildLoanClassifyChange(loanAccountInfo, bussinessDate, loanAccountInfo.getRemainPrincipal(), alterClass);
                saveBeanOrder.getSaveClassifyList().add(tlLoanClassifyChange);
            }

            saveBeanList.add(saveBeanOrder);
        }

        // 11.更新客户额度
        this.updateCustLmt(saveBeanList, loanMainInfoList);

        //更新合作方额度
        this.updatePartnerLimit(saveBeanList, loanMainInfoList, paymentLog.getSeqNo());

        // 13.保存所信息
        PaymentSaveBean bean = this.dealAllAccts(saveBeanList, tlTransactionInfo);

        return bean;
    }

    /**
     * 账单逾期还款分配
     *
     * @param paymentLog       还款流水
     * @param loanMainInfoList 贷款信息列表
     * @param bussinessDate    业务日期
     * @throws TxnException 自定义异常
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void dealBillOverDuePay(TlPaymentLog paymentLog, List<AmLoanMainInfo> loanMainInfoList, Date bussinessDate) throws TxnException {
        // 12.保存所信息
        PaymentSaveBean bean = this.dealBillOverDuePayNotSave(paymentLog, loanMainInfoList, bussinessDate);
        this.saveAllAccts(bean);
    }

    /**
     * 账单正常还款
     *
     * @param paymentLog    还款流水
     * @param loanMainInfos 贷款信息列表
     * @param bussinessDate 业务日期
     * @throws TxnException 自定义异常
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void dealBillPay(TlPaymentLog paymentLog, List<AmLoanMainInfo> loanMainInfos, Date bussinessDate) throws TxnException {
        /* 还款额 */
        BigDecimal paymentAmt = paymentLog.getAmount();
        /* 还款分配入库bean */
        List<PaymentSaveBean> saveBeanList = new ArrayList<>();

        // 1.在还款分配历史表 tl_payment_allocation_log生成08还款交易账户
        TlPaymentAllocationLog totalPaymentLog = tlPaymentAllocationLogService.buildTlPaymentAllocationLog(paymentLog,
                loanMainInfos.get(0), bussinessDate);
        // 2.生成还款交易流水
        TlTransactionInfo tlTransactionInfo = tlTransactionInfoService.buildTlTransactionInfo(totalPaymentLog);

        for (AmLoanMainInfo loanMainInfo : loanMainInfos) {
            PaymentSaveBean saveBean = new PaymentSaveBean();
            saveBean.setSavaPaymentLog(totalPaymentLog);

            //获取订单账户信息
            AmLoanAccountInfo loanAccountInfo = Optional.ofNullable(extAmLoanAccountInfoMapper.selectByLoanId(loanMainInfo.getLoanId(),
                    loanMainInfo.getCustomerId(), loanMainInfo.getTenantId()))
                    .orElseThrow(() -> {
                        logger.error("订单交易账户为空，loanId:{}", loanMainInfo.getLoanId());
                        return new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
                    });
            logger.info("订单账户信息loanAccountInfo:{}", BeanUtils.toMap(loanAccountInfo));

            // 2.获取还款需要处理的还款计划
            this.getNeedDealPlan(saveBean, loanMainInfo, Arrays.asList(TransBizConstant.LOAN_PLAN_STATUS_1,
                    TransBizConstant.LOAN_PLAN_STATUS_2));

            // 3.过滤还款计划
            this.filterPlanMap(saveBean, loanMainInfo, bussinessDate);

            // 4.获取逾期本金，利息，罚息，罚息复利,费用交易账户
            this.getAlreadyCastAcct(saveBean, loanMainInfo
                    , Arrays.asList(TransBizConstant.ACCOUNT_STATUS_1, TransBizConstant.ACCOUNT_STATUS_2));

            // 5.罚息,罚息复利累计账户抛帐，获取生成的罚息,罚息复利交易账户,并关闭累计
            this.castAccuAcct(saveBean, loanMainInfo, loanAccountInfo, Arrays.asList(TransBizConstant.INTEREST_ACCRUAL_TYPE_10,
                    TransBizConstant.INTEREST_ACCRUAL_TYPE_20, TransBizConstant.INTEREST_ACCRUAL_TYPE_66), bussinessDate);

            // 6.计算欠款
            DebtBean debtBean = debtCalculationService.calculationDebt(saveBean.getNeedDealAcctList());

            // 7.将这些交易账户按照分配顺序真正分配，生成09交易账户 并关闭交易账户
            paymentAmt = this.repaymentAllocation(saveBean, loanMainInfo, loanAccountInfo, debtBean, paymentAmt, bussinessDate);

            // 8. 生成09对应的gl流水
            List<TlGlInterfaceInfo> saveGlList = glInterfaceService.buildTlGlInterfaceInfoList(saveBean.getPaymentLogSaveList(), loanMainInfo, loanAccountInfo);
            saveBean.addSaveGlList(saveGlList);

            // 9.处理还款计划
            this.dealPlan(saveBean, bussinessDate);

            // 11.处理订单
            this.dealOrder(saveBean, loanMainInfo, loanAccountInfo);

            // 12 判断是否记录五级分类变动
            if (null != saveBean.getUpdateLoan() && (TransBizConstant.LOAN_STATUS_7.equals(saveBean.getUpdateLoan().getAccountStatus()) ||
                    TransBizConstant.LOAN_STATUS_8.equals(saveBean.getUpdateLoan().getAccountStatus()))) {
                String alterClass = saveBean.getUpdateLoan().getLoanClassify();
                if (StringUtils.isBlank(alterClass)) {
                    //如果为空表示五级分类不可逆，只是保存一条流水
                    alterClass = loanAccountInfo.getLoanClassify();
                }
                TlLoanClassifyChange tlLoanClassifyChange = tlLoanClassifyService.buildLoanClassifyChange(loanAccountInfo, bussinessDate, loanAccountInfo.getRemainPrincipal(), alterClass);
                saveBean.getSaveClassifyList().add(tlLoanClassifyChange);
            }

            saveBeanList.add(saveBean);
        }

        // 12.更新客户额度
        this.updateCustLmt(saveBeanList, loanMainInfos);

        //更新合作方额度
        this.updatePartnerLimit(saveBeanList, loanMainInfos, paymentLog.getSeqNo());

        PaymentSaveBean bean = this.dealAllAccts(saveBeanList, tlTransactionInfo);
        this.saveAllAccts(bean);
    }

    /**
     * 账单提前还当期
     *
     * @param paymentLog    还款流水
     * @param loanMainInfos 贷款信息列表
     * @param bussinessDate 业务日期
     * @throws TxnException 自定义异常
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void dealBillPrepayCurr(TlPaymentLog paymentLog, List<AmLoanMainInfo> loanMainInfos, Date bussinessDate) throws TxnException {
        List<AmLoanAccountInfo> loanAccountInfoList = extAmLoanAccountInfoMapper.selectByCustAndChannelAndStatus(paymentLog.getCustomerId(), paymentLog.getChannel(),
                paymentLog.getTenantId(), Arrays.asList(TransBizConstant.LOAN_STATUS_1, TransBizConstant.LOAN_STATUS_2));
        PaymentSaveBean saveBean = this.dealBillPrepayCurrNotSave(paymentLog, loanMainInfos, loanAccountInfoList, bussinessDate);
        this.saveAllAccts(saveBean);
    }

    /**
     * 账单提前还当期
     *
     * @param paymentLog    还款流水
     * @param loanMainInfos 贷款信息列表
     * @param bussinessDate 业务日期
     * @throws TxnException 自定义异常
     */

    private PaymentSaveBean dealBillPrepayCurrNotSave(TlPaymentLog paymentLog, List<AmLoanMainInfo> loanMainInfos, List<AmLoanAccountInfo> loanAccountInfoList, Date bussinessDate) throws TxnException {
        // 1.在还款分配历史表 tl_payment_allocation_log生成08还款交易账户
        TlPaymentAllocationLog totalPaymentLog = tlPaymentAllocationLogService.buildTlPaymentAllocationLog(paymentLog,
                loanMainInfos.get(0), bussinessDate);
        // 2.生成还款交易流水
        TlTransactionInfo tlTransactionInfo = tlTransactionInfoService.buildTlTransactionInfo(totalPaymentLog);

        /* 还款额 */
        BigDecimal paymentAmt = paymentLog.getAmount();
        /* 还款分配入库bean */
        List<PaymentSaveBean> saveBeanList = new ArrayList<>();
        BigDecimal totalDebt = BigDecimal.ZERO;
        PaymentSaveBean saveBean = new PaymentSaveBean();
        saveBean.setSavaPaymentLog(totalPaymentLog);
        Map<String, DebtBean> debtBeanMap = new HashMap<>();

        //循环处理每个订单的分配前准备
        for (AmLoanMainInfo loanMainInfo : loanMainInfos) {
            logger.info("提前还当期还款循环分配订单开始 loanMainInfo:{}", BeanUtils.toMap(loanMainInfo));
            //获取订单账户信息
            AmLoanAccountInfo loanAccountInfo = loanAccountInfoList.stream().filter(loan -> loan.getLoanId().equals(loanMainInfo.getLoanId())).findFirst().get();
            logger.info("订单账户信息loanAccountInfo:{}", BeanUtils.toMap(loanAccountInfo));

            // 2.获取还款需要处理的还款计划
            this.getNeedDealPlan(saveBean, loanMainInfo, Arrays.asList(TransBizConstant.LOAN_PLAN_STATUS_1, TransBizConstant.LOAN_PLAN_STATUS_0));

            // 4.获取逾期本金，利息交易账户
            this.getAlreadyCastAcct(saveBean, loanMainInfo
                    , Collections.singletonList(TransBizConstant.ACCOUNT_STATUS_1));

            // 5.利息累计账户抛帐，获取生成的利息交易账户,并关闭累计
            this.castAccuAcct(saveBean, loanMainInfo, loanAccountInfo, Collections.singletonList(TransBizConstant.INTEREST_ACCRUAL_TYPE_66), bussinessDate);

            // 6.计算欠款
            List<AmAccountMainInfo> needDealAcctList = saveBean.getNeedDealAcctList().stream().filter(acct -> loanMainInfo.getLoanId().equals(acct.getLoanId())).collect(Collectors.toList());
            DebtBean debtBean = debtCalculationService.calculationDebt(needDealAcctList);
            debtBeanMap.put(loanMainInfo.getLoanId(), debtBean);
            totalDebt = totalDebt.add(debtBean.getTotalAmount());
        }


        // 6.将这些交易账户按照分配顺序真正分配，生成09交易账户，并关闭交易账户
        this.repaymentBillAllocation(saveBean, loanMainInfos, loanAccountInfoList, totalDebt, paymentAmt, bussinessDate);
        // 7. 生成09对应的gl流水
        List<TlGlInterfaceInfo> saveGlList = glInterfaceService.buildTlGlInterfaceInfoList(saveBean.getPaymentLogSaveList(), loanMainInfos, loanAccountInfoList);
        saveBean.addSaveGlList(saveGlList);

        for (AmLoanMainInfo loanMainInfo : loanMainInfos) {
            String loanId = loanMainInfo.getLoanId();
            AmLoanAccountInfo loanAccountInfo = loanAccountInfoList.stream().filter(loan -> loan.getLoanId().equals(loanId)).findFirst().get();

            //生成有后续每个订单处理的saveBean
            PaymentSaveBean saveBeanOrder = this.afterBillAllocation(saveBean, loanMainInfo);


            PrProductTable prProductTable = productTableService.findProByProIdAndOrgId(loanMainInfo.getProductId(), loanMainInfo.getOrganizationId(), loanMainInfo.getTenantId());
            // 9.处理还款计划
            DebtBean debtBean = debtBeanMap.get(loanId);
            this.dealBillPrepayCurrPlan(saveBeanOrder, loanMainInfo, loanAccountInfo, bussinessDate, prProductTable, debtBean);

            // 9.处理订单
            this.dealOrder(saveBeanOrder, loanMainInfo, loanAccountInfo);

            // 12.贴息产品生成贴息还款流水
            this.dealWaive(saveBeanOrder, loanMainInfo, loanAccountInfo, prProductTable, bussinessDate);

            // 11 判断是否记录五级分类变动
            if (null != saveBeanOrder.getUpdateLoan() && (TransBizConstant.LOAN_STATUS_7.equals(saveBeanOrder.getUpdateLoan().getAccountStatus()) ||
                    TransBizConstant.LOAN_STATUS_8.equals(saveBeanOrder.getUpdateLoan().getAccountStatus()))) {
                String alterClass = saveBeanOrder.getUpdateLoan().getLoanClassify();
                if (StringUtils.isBlank(alterClass)) {
                    //如果为空表示五级分类不可逆，只是保存一条流水
                    alterClass = loanAccountInfo.getLoanClassify();
                }
                TlLoanClassifyChange tlLoanClassifyChange = tlLoanClassifyService.buildLoanClassifyChange(loanAccountInfo, bussinessDate, loanAccountInfo.getRemainPrincipal(), alterClass);
                saveBeanOrder.getSaveClassifyList().add(tlLoanClassifyChange);
            }

            saveBeanList.add(saveBeanOrder);
        }


        // 12.更新客户额度
        this.updateCustLmt(saveBeanList, loanMainInfos);

        //更新合作方额度
        this.updatePartnerLimit(saveBeanList, loanMainInfos, paymentLog.getSeqNo());

        // 13.保存所信息
        // 12.保存所信息
        PaymentSaveBean bean = this.dealAllAccts(saveBeanList, tlTransactionInfo);
        return bean;
    }

    /**
     * 账单提前还款
     *
     * @param paymentLog    还款流水
     * @param loanMainInfos 贷款信息列表
     * @param bussinessDate 业务日期
     * @throws TxnException 自定义异常
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void dealBillPrepay(TlPaymentLog paymentLog, List<AmLoanMainInfo> loanMainInfos, Date bussinessDate) throws TxnException {
        // 1.在还款分配历史表 tl_payment_allocation_log生成08还款交易账户
        TlPaymentAllocationLog totalPaymentLog = tlPaymentAllocationLogService.buildTlPaymentAllocationLog(paymentLog,
                loanMainInfos.get(0), bussinessDate);
        // 2.生成还款交易流水
        TlTransactionInfo tlTransactionInfo = tlTransactionInfoService.buildTlTransactionInfo(totalPaymentLog);

        /* 还款额 */
        BigDecimal paymentAmt = paymentLog.getAmount();
        /* 还款分配入库bean */
        List<PaymentSaveBean> saveBeanList = new ArrayList<>();
        BigDecimal totalDebt = BigDecimal.ZERO;
        List<AmLoanAccountInfo> loanAccountInfoList = new ArrayList<>();
        PaymentSaveBean saveBean = new PaymentSaveBean();
        saveBean.setSavaPaymentLog(totalPaymentLog);
        Map<String, DebtBean> debtBeanMap = new HashMap<>();


        //获取客户信息
        CmCustomerSecondLevel customer = customerService.getSecondByChannelAndCustomerIdAndTensnId(paymentLog.getChannel(),
                paymentLog.getCustomerId(), paymentLog.getTenantId(), null);
        Integer statementDd = customer.getStatementDd();
        Integer paymentDd = customer.getPaymentDd();
        CurrentStatementBean currentStatementBean = loanHandlerImpl.getCurrentStatement(paymentLog.getProductId(), statementDd, paymentDd, bussinessDate);
        //获取当期还款日
        Date onDuePaymentDate = currentStatementBean.getPaymentDate();
        //获取下期期还款日
        Date nextPaymentDate = DateUtils.getDateByMonth(onDuePaymentDate, 1);

        //循环处理每个订单的分配前准备
        for (AmLoanMainInfo loanMainInfo : loanMainInfos) {
            logger.info("提前还款循环分配订单开始 loanMainInfo:{}", BeanUtils.toMap(loanMainInfo));
            //获取订单账户信息
            AmLoanAccountInfo loanAccountInfo = Optional.ofNullable(extAmLoanAccountInfoMapper.selectByLoanId(loanMainInfo.getLoanId(),
                    loanMainInfo.getCustomerId(), loanMainInfo.getTenantId()))
                    .orElseThrow(() -> {
                        logger.error("订单交易账户为空，loanId:{}", loanMainInfo.getLoanId());
                        return new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
                    });
            logger.info("订单账户信息loanAccountInfo:{}", BeanUtils.toMap(loanAccountInfo));
            loanAccountInfoList.add(loanAccountInfo);

            // 2.获取还款需要处理的还款计划
            this.getNeedDealPlan(saveBean, loanMainInfo, Arrays.asList(TransBizConstant.LOAN_PLAN_STATUS_1, TransBizConstant.LOAN_PLAN_STATUS_0));

            // 4.获取正常，逾期本金，利息交易账户
            this.getAlreadyCastAcct(saveBean, loanMainInfo
                    , Arrays.asList(TransBizConstant.ACCOUNT_STATUS_1, TransBizConstant.ACCOUNT_STATUS_2));

            // 5.利息，罚息复利累计账户抛帐，获取生成的利息交易账户,并关闭累计
            this.castAccuAcct(saveBean, loanMainInfo, loanAccountInfo, Arrays.asList(TransBizConstant.INTEREST_ACCRUAL_TYPE_10,
                    TransBizConstant.INTEREST_ACCRUAL_TYPE_20, TransBizConstant.INTEREST_ACCRUAL_TYPE_66), bussinessDate);

            // 6.下期账单还款计划如果未抛，则将本金账户抛帐,否则不处理
            DelqParamBean delqParamBean = delqService.buildDqlqAccountBean(loanMainInfo.getTenantId(), loanMainInfo.getDelqTableId());
            this.castNextStatmentPrinAcct(saveBean, loanMainInfo, loanAccountInfo, delqParamBean, nextPaymentDate, bussinessDate);

            // 7.计算欠款
            List<AmAccountMainInfo> needDealAcctList = saveBean.getNeedDealAcctList().stream().filter(acct -> loanMainInfo.getLoanId().equals(acct.getLoanId())).collect(Collectors.toList());
            DebtBean debtBean = debtCalculationService.calculationDebt(needDealAcctList);
            debtBeanMap.put(loanMainInfo.getLoanId(), debtBean);
            totalDebt = totalDebt.add(debtBean.getTotalAmount());
        }


        // 8.将这些交易账户按照分配顺序真正分配，生成09交易账户，并关闭交易账户
        this.repaymentBillPreAllocation(saveBean, loanMainInfos, loanAccountInfoList, totalDebt, paymentAmt, bussinessDate);
        // 7. 生成09对应的gl流水
        List<TlGlInterfaceInfo> saveGlList = glInterfaceService.buildTlGlInterfaceInfoList(saveBean.getPaymentLogSaveList(), loanMainInfos, loanAccountInfoList);
        saveBean.addSaveGlList(saveGlList);

        for (AmLoanMainInfo loanMainInfo : loanMainInfos) {
            String loanId = loanMainInfo.getLoanId();
            AmLoanAccountInfo loanAccountInfo = loanAccountInfoList.stream().filter(loan -> loan.getLoanId().equals(loanId)).findFirst().get();

            //生成有后续每个订单处理的saveBean
            PaymentSaveBean saveBeanOrder = this.afterBillAllocation(saveBean, loanMainInfo);


            PrProductTable prProductTable = productTableService.findProByProIdAndOrgId(loanMainInfo.getProductId(), loanMainInfo.getOrganizationId(), loanMainInfo.getTenantId());
            // 9.处理还款计划
            DebtBean debtBean = debtBeanMap.get(loanId);
            this.dealBillPrepayPlan(saveBeanOrder, loanMainInfo, loanAccountInfo, bussinessDate, prProductTable, debtBean, onDuePaymentDate, nextPaymentDate);

            this.reCastAcct(saveBeanOrder, loanMainInfo, loanAccountInfo, bussinessDate);

            // 9.处理订单
            this.dealOrder(saveBeanOrder, loanMainInfo, loanAccountInfo);

            // 12.贴息产品生成贴息还款流水
            this.dealWaive(saveBeanOrder, loanMainInfo, loanAccountInfo, prProductTable, bussinessDate);

            // 11 判断是否记录五级分类变动
            if (null != saveBeanOrder.getUpdateLoan() && (TransBizConstant.LOAN_STATUS_7.equals(saveBeanOrder.getUpdateLoan().getAccountStatus()) ||
                    TransBizConstant.LOAN_STATUS_8.equals(saveBeanOrder.getUpdateLoan().getAccountStatus()))) {
                String alterClass = saveBeanOrder.getUpdateLoan().getLoanClassify();
                if (StringUtils.isBlank(alterClass)) {
                    //如果为空表示五级分类不可逆，只是保存一条流水
                    alterClass = loanAccountInfo.getLoanClassify();
                }
                TlLoanClassifyChange tlLoanClassifyChange = tlLoanClassifyService.buildLoanClassifyChange(loanAccountInfo, bussinessDate, loanAccountInfo.getRemainPrincipal(), alterClass);
                saveBeanOrder.getSaveClassifyList().add(tlLoanClassifyChange);
            }

            saveBeanList.add(saveBeanOrder);
        }


        // 12.更新客户额度
        this.updateCustLmt(saveBeanList, loanMainInfos);

        //更新合作方额度
        this.updatePartnerLimit(saveBeanList, loanMainInfos, paymentLog.getSeqNo());

        // 13.保存所信息
        PaymentSaveBean bean = this.dealAllAccts(saveBeanList, tlTransactionInfo);
        this.saveAllAccts(bean);
    }

    /**
     * 逾期提前还当期处理
     *
     * @param tlPaymentLog
     * @param loanList
     * @param bussinessDate
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void dealBillOverPrepayCurr(TlPaymentLog tlPaymentLog, List<AmLoanMainInfo> loanList, Date bussinessDate) throws TxnException {
        List<PaymentSaveBean> saveBeanList = new ArrayList<>();
        //还款金额
        BigDecimal payAmt = tlPaymentLog.getAmount();
        //生成08交易账户
        TlPaymentAllocationLog totalPaymentLog = tlPaymentAllocationLogService.buildTlPaymentAllocationLog(tlPaymentLog,
                loanList.get(0), bussinessDate);
        // 2.生成还款交易流水
        TlTransactionInfo tlTransactionInfo = tlTransactionInfoService.buildTlTransactionInfo(totalPaymentLog);

        List<AmLoanAccountInfo> loanAccountInfoList = extAmLoanAccountInfoMapper.selectByCustAndChannelAndStatus(tlPaymentLog.getCustomerId(), tlPaymentLog.getChannel(),
                tlPaymentLog.getTenantId(), Arrays.asList(TransBizConstant.LOAN_STATUS_1, TransBizConstant.LOAN_STATUS_2));

        // 3.计算逾期欠款金额
        BigDecimal overDueAmt = BigDecimal.ZERO;
        for (AmLoanMainInfo loanMainInfo : loanList) {
            AmLoanAccountInfo loanAccountInfo = loanAccountInfoList.stream().filter(loan -> loan.getLoanId().equals(loanMainInfo.getLoanId())).findFirst().get();
            DebtBean debtBean = debtCalculationService.getBalance(loanAccountInfo, bussinessDate, true, null, null, false);
            overDueAmt = overDueAmt.add(debtBean.getTotalAmount());
        }

        //先还逾期部分
        tlPaymentLog.setAmount(overDueAmt);
        PaymentSaveBean overSaveBean = this.dealBillOverDuePayNotSave(tlPaymentLog, loanList, bussinessDate);
        saveBeanList.add(overSaveBean);

        for (AmLoanAccountInfo loanInfo : loanAccountInfoList) {
            overSaveBean.getUpdateLoanList().stream().filter(updateLoan -> updateLoan.getId().equals(loanInfo.getId())).findFirst().ifPresent(
                    updateLoan -> BeanMapping.copyUpdate(loanInfo, updateLoan)
            );
        }

        //还有剩余做提前还当期
        if (overDueAmt.compareTo(payAmt) < 0) {
            tlPaymentLog.setAmount(payAmt.subtract(overDueAmt));
            PaymentSaveBean preSaveBean = this.dealBillPrepayCurrNotSave(tlPaymentLog, loanList, loanAccountInfoList, bussinessDate);
            saveBeanList.add(preSaveBean);
        }


        PaymentSaveBean bean = this.dealAllAccts(saveBeanList, tlTransactionInfo);
        bean.getTransactionInfoList().removeIf(log -> TransBizConstant.TXN_CODE_PAYMENT_DETAIL.equals(log.getTxnCode()));
        bean.getTransactionInfoList().add(tlTransactionInfo);
        bean.setSavaPaymentLog(totalPaymentLog);
        bean.getPaymentLogSaveList().forEach(log -> log.setOriginalAccountId(totalPaymentLog.getAccountId()));
        //保存入库
        this.saveAllAccts(bean);
    }

    /**
     * 退款处理
     *
     * @param tlPaymentLog
     * @param loanList
     * @param bussinessDate
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void dealRefund(TlPaymentLog tlPaymentLog, List<AmLoanMainInfo> loanList, Date bussinessDate) throws TxnException {
        List<PaymentSaveBean> saveBeanList = new ArrayList<>();
        //生成08交易账户
        TlPaymentAllocationLog totalPaymentLog = tlPaymentAllocationLogService.buildTlPaymentAllocationLog(tlPaymentLog,
                loanList.get(0), bussinessDate);
        // 2.生成还款交易流水
        TlTransactionInfo tlTransactionInfo = tlTransactionInfoService.buildTlTransactionInfo(totalPaymentLog);
        BigDecimal payAmt = tlPaymentLog.getAmount();

        TlTransactionInfo refundTransaction = extTlTransactionInfoMapper.selectBySeqNo(tlPaymentLog.getRefNo(), tlPaymentLog.getCustomerId(), tlPaymentLog.getTenantId());
        String refundLoanId = refundTransaction.getLoanId();
        //参与分期
        TlLoanStagingMapping stagingMapping = extTlLoanStagingMappingMapper.selectByCustIdAndOrigLoanId(tlPaymentLog.getCustomerId(), tlPaymentLog.getTenantId(), refundLoanId);


        if (null != stagingMapping) {
            refundLoanId = stagingMapping.getStagingLoanId();
        }

        String finalRefundLoanId = refundLoanId;
        loanList.stream().filter(info -> finalRefundLoanId.equals(info.getLoanId())).findFirst().ifPresent(
                info -> Collections.swap(loanList, 0, loanList.indexOf(info)));


        List<AmLoanAccountInfo> loanAccountInfoList = extAmLoanAccountInfoMapper.selectByCustAndChannelAndStatus(tlPaymentLog.getCustomerId(), tlPaymentLog.getChannel(),
                tlPaymentLog.getTenantId(), Arrays.asList(TransBizConstant.LOAN_STATUS_1, TransBizConstant.LOAN_STATUS_2));

        //先分配退货或者退货分期的订单
        for (AmLoanMainInfo loan : loanList) {
            //如果存在逾期订单先结清逾期部分
            AmLoanAccountInfo loanAccountInfo = loanAccountInfoList.stream().filter(loanInfo -> loanInfo.getLoanId().equals(loan.getLoanId())).findFirst().get();
            if (TransBizConstant.LOAN_STATUS_2.equals(loanAccountInfo.getAccountStatus())) {
                PaymentSaveBean overSaveBean = this.dealOverPayNotSave(tlPaymentLog, Collections.singletonList(loan), bussinessDate);
                BigDecimal overPayAmt = overSaveBean.getPaymentLogSaveList().stream().map(TlPaymentAllocationLog::getPostingAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
                this.dealSavenBean(overSaveBean, totalPaymentLog, finalRefundLoanId);
                saveBeanList.add(overSaveBean);

                payAmt = payAmt.subtract(overPayAmt);
                tlPaymentLog.setAmount(payAmt);
                BeanMapping.copyUpdate(loanAccountInfo, overSaveBean.getUpdateLoanList().get(0));
                if (payAmt.compareTo(BigDecimal.ZERO) <= 0) {
                    break;
                }
            }

            if (TransBizConstant.LOAN_STATUS_7.equals(loanAccountInfo.getAccountStatus()) || TransBizConstant.LOAN_STATUS_8.equals(loanAccountInfo.getAccountStatus())) {
                continue;
            }
            //未逾期的部分再做提前还款
            PaymentSaveBean saveBean = this.dealPrePayNotSave(tlPaymentLog, loan, loanAccountInfo, bussinessDate);
            BigDecimal realPayAmt = saveBean.getPaymentLogSaveList().stream().map(TlPaymentAllocationLog::getPostingAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
            this.dealSavenBean(saveBean, totalPaymentLog, finalRefundLoanId);
            saveBeanList.add(saveBean);


            payAmt = payAmt.subtract(realPayAmt);
            tlPaymentLog.setAmount(payAmt);
            if (payAmt.compareTo(BigDecimal.ZERO) <= 0) {
                break;
            }
        }

        PaymentSaveBean bean = this.dealAllAccts(saveBeanList, tlTransactionInfo);
        bean.setSavaPaymentLog(totalPaymentLog);
        //保存入库
        this.saveAllAccts(bean);
    }


    private void dealSavenBean(PaymentSaveBean saveBean, TlPaymentAllocationLog totalPaymentLog, String finalRefundLoanId) throws TxnException {
        //舍弃其他的自己生成的08交易账户
        saveBean.setSavaPaymentLog(null);
        saveBean.getPaymentLogSaveList().forEach(log -> log.setOriginalAccountId(totalPaymentLog.getAccountId()));

        //原订单本金交易码用退款交易吗
        saveBean.getPaymentLogSaveList().stream().filter(log -> log.getLoanId().equals(finalRefundLoanId))
                .filter(log -> TransBizConstant.ACCOUNT_TYPE_06.equals(log.getConnectAccountType()))
                .forEach(log -> log.setTxnCode(TransBizConstant.TXN_CODE_REFUND));

        PrTransactionCodeTable refundTxnCode = transactionCodeTableService.findByCodeAndTenantId(TransBizConstant.TXN_CODE_REFUND, totalPaymentLog.getTenantId());
        saveBean.getSaveGlList().stream().filter(gl -> gl.getLoanId().equals(finalRefundLoanId))
                .filter(gl -> gl.getTxnCode().equals(TransBizConstant.TXN_CODE_INST_PYMT))
                .forEach(gl -> {
                    gl.setTxnCode(TransBizConstant.TXN_CODE_REFUND);
                    gl.setTxnDesc(refundTxnCode.getChineseName());
                });

        saveBean.getTransactionInfoList().removeIf(log -> TransBizConstant.TXN_CODE_PAYMENT_DETAIL.equals(log.getTxnCode()));
    }


    /**
     * 代偿还款分配
     *
     * @param paymentLog       还款流水
     * @param loanMainInfoList 贷款信息列表
     * @param bussinessDate    业务日期
     * @throws TxnException 自定义异常
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void dealCmpsPay(TlPaymentLog paymentLog, List<AmLoanMainInfo> loanMainInfoList, Date bussinessDate) throws TxnException {
        logger.info("代偿还款分配开始 paymentLog:{}", BeanUtils.toMap(paymentLog));
        // 1.在还款分配历史表 tl_payment_allocation_log生成08还款交易账户 如果是代偿回购 更新代偿标识
        TlPaymentAllocationLog totalPaymentLog = tlPaymentAllocationLogService.buildTlPaymentAllocationLog(paymentLog, loanMainInfoList.get(0), bussinessDate);
        // 2.生成还款交易流水
        TlTransactionInfo tlTransactionInfo = tlTransactionInfoService.buildTlTransactionInfo(totalPaymentLog);
        //总还款金额
        BigDecimal paymentAmt = totalPaymentLog.getPostingAmount();
        List<PaymentSaveBean> saveBeanList = new ArrayList<>();
        //循环处理每个订单
        for (AmLoanMainInfo loanMainInfo : loanMainInfoList) {
            logger.info("代偿还款循环分配订单开始 loanMainInfo:{}", BeanUtils.toMap(loanMainInfo));
            PaymentSaveBean saveBean = new PaymentSaveBean();
            saveBean.setSavaPaymentLog(totalPaymentLog);
            //获取订单账户信息
            AmLoanAccountInfo loanAccountInfo = Optional.ofNullable(extAmLoanAccountInfoMapper.selectByLoanId(loanMainInfo.getLoanId(),
                    loanMainInfo.getCustomerId(), loanMainInfo.getTenantId()))
                    .orElseThrow(() -> {
                        logger.error("订单交易账户为空，loanId:{}", loanMainInfo.getLoanId());
                        return new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
                    });
            logger.info("订单账户信息loanAccountInfo:{}", BeanUtils.toMap(loanAccountInfo));
            //添加代偿回购判断 方便处理 代偿不会更新订单账户 代偿标识
            loanAccountInfo.setCompensatoryFlag(TransBizConstant.COMPENSATORY_FLAG_1);//代偿标识


            // 2.获取还款需要处理的还款计划
            this.getNeedDealPlan(saveBean, loanMainInfo, Collections.singletonList(TransBizConstant.LOAN_PLAN_STATUS_2));

            // 3.获取逾期本金，利息，罚息，罚息复利,费用交易账户
            this.getAlreadyCastAcct(saveBean, loanMainInfo, Arrays.asList(TransBizConstant.ACCOUNT_STATUS_1, TransBizConstant.ACCOUNT_STATUS_2));

            // 4.罚息,罚息复利累计账户抛帐，获取生成的罚息,罚息复利交易账户,并关闭累计  添加代偿回购标识逻辑
            this.castAccuAcct(saveBean, loanMainInfo, loanAccountInfo, Arrays.asList(TransBizConstant.INTEREST_ACCRUAL_TYPE_10
                    , TransBizConstant.INTEREST_ACCRUAL_TYPE_20), bussinessDate);

            // 5.计算欠款
            DebtBean debtBean = debtCalculationService.calculationDebt(saveBean.getNeedDealAcctList());

            // 6.将这些交易账户按照分配顺序真正分配，生成09交易账户，并关闭交易账户   添加代偿回购标识逻辑
            paymentAmt = this.repaymentAllocation(saveBean, loanMainInfo, loanAccountInfo, debtBean, paymentAmt, bussinessDate);

            // 7. 生成09对应的gl流水
            List<TlGlInterfaceInfo> saveGlList = glInterfaceService.buildTlGlInterfaceInfoList(saveBean.getPaymentLogSaveList(), loanMainInfo, loanAccountInfo);
            saveBean.addSaveGlList(saveGlList);

            // 8.处理还款计划 添加代偿标识判断
            this.dealPlan(saveBean, bussinessDate);

            // 9.处理订单 添加代偿标识 总代偿次数判断
            this.dealOrder(saveBean, loanMainInfo, loanAccountInfo);

            // 10.处理延滞
            DelqParamBean delqParamBean = delqService.buildDqlqAccountBean(loanMainInfo.getTenantId(), loanMainInfo.getDelqTableId());
            this.dealDelq(saveBean, loanMainInfo, loanAccountInfo, delqParamBean, bussinessDate);

            // 11 判断是否记录五级分类变动
            if (TransBizConstant.LOAN_STATUS_7.equals(saveBean.getUpdateLoan().getAccountStatus()) ||
                    TransBizConstant.LOAN_STATUS_8.equals(saveBean.getUpdateLoan().getAccountStatus())) {
                String alterClass = saveBean.getUpdateLoan().getLoanClassify();
                if (StringUtils.isBlank(alterClass)) {
                    //如果为空表示五级分类不可逆，只是保存一条流水
                    alterClass = loanAccountInfo.getLoanClassify();
                }
                TlLoanClassifyChange tlLoanClassifyChange = tlLoanClassifyService.buildLoanClassifyChange(loanAccountInfo, bussinessDate, loanAccountInfo.getRemainPrincipal(), alterClass);
                saveBean.getSaveClassifyList().add(tlLoanClassifyChange);
            }

            saveBeanList.add(saveBean);
        }

        // 11.更新客户额度
        this.updateCustLmt(saveBeanList, loanMainInfoList);

        //更新合作方额度
        this.updatePartnerLimit(saveBeanList, loanMainInfoList, paymentLog.getSeqNo());

        // 12.保存所信息
        PaymentSaveBean bean = this.dealAllAccts(saveBeanList, tlTransactionInfo);
        this.saveAllAccts(bean);
    }

    /**
     * 回购还款分配
     *
     * @param paymentLog       还款流水
     * @param loanMainInfoList 贷款信息列表
     * @param bussinessDate    业务日期
     * @throws TxnException 自定义异常
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void dealCrtPay(TlPaymentLog paymentLog, List<AmLoanMainInfo> loanMainInfoList, Date bussinessDate) throws TxnException {
        logger.info("回购还款分配开始 paymentLog:{}", BeanUtils.toMap(paymentLog));
        // 1.在还款分配历史表 tl_payment_allocation_log生成08还款交易账户  如果是代偿回购 更新代偿标识
        TlPaymentAllocationLog totalPaymentLog = tlPaymentAllocationLogService.buildTlPaymentAllocationLog(paymentLog, loanMainInfoList.get(0), bussinessDate);
        // 2.生成还款交易流水
        TlTransactionInfo tlTransactionInfo = tlTransactionInfoService.buildTlTransactionInfo(totalPaymentLog);

        BigDecimal paymentAmt = totalPaymentLog.getPostingAmount();
        List<PaymentSaveBean> saveBeanList = new ArrayList<>();
        //循环处理每个订单
        for (AmLoanMainInfo loanMainInfo : loanMainInfoList) {
            logger.info("回购循环分配订单开始 loanMainInfo:{}", BeanUtils.toMap(loanMainInfo));
            PaymentSaveBean saveBean = new PaymentSaveBean();
            saveBean.setSavaPaymentLog(totalPaymentLog);

            //获取订单账户信息
            AmLoanAccountInfo loanAccountInfo = Optional.ofNullable(extAmLoanAccountInfoMapper.selectByLoanId(loanMainInfo.getLoanId(),
                    loanMainInfo.getCustomerId(), loanMainInfo.getTenantId()))
                    .orElseThrow(() -> {
                        logger.error("订单交易账户为空，loanId:{}", loanMainInfo.getLoanId());
                        return new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
                    });
            logger.info("订单账户信息loanAccountInfo:{}", BeanUtils.toMap(loanAccountInfo));
            //添加代偿标识
            loanAccountInfo.setCompensatoryFlag(TransBizConstant.COMPENSATORY_FLAG_1);

            // 2.获取还款需要处理的还款计划
            this.getNeedDealPlan(saveBean, loanMainInfo, Arrays.asList(TransBizConstant.LOAN_PLAN_STATUS_0
                    , TransBizConstant.LOAN_PLAN_STATUS_1, TransBizConstant.LOAN_PLAN_STATUS_2));

            // 3.获取逾期本金，利息，罚息，罚息复利,费用交易账户
            this.getAlreadyCastAcct(saveBean, loanMainInfo
                    , Arrays.asList(TransBizConstant.ACCOUNT_STATUS_1, TransBizConstant.ACCOUNT_STATUS_2));

            // 4.利息,罚息,罚息复利累计账户抛帐，获取生成的利息,罚息,罚息复利交易账户,并关闭累计  //添加代偿标识判断
            this.castAccuAcct(saveBean, loanMainInfo, loanAccountInfo, Arrays.asList(TransBizConstant.INTEREST_ACCRUAL_TYPE_66,
                    TransBizConstant.INTEREST_ACCRUAL_TYPE_10, TransBizConstant.INTEREST_ACCRUAL_TYPE_20), bussinessDate);

            DelqParamBean delqParamBean = delqService.buildDqlqAccountBean(loanMainInfo.getTenantId(), loanMainInfo.getDelqTableId());
            // 5.未抛帐的期数本金抛帐，获取生成的本金交易账户  //添加代偿标识判断
            this.castNonCastPrinAcct(saveBean, loanMainInfo, loanAccountInfo, delqParamBean, bussinessDate);

            // 6.计算欠款
            DebtBean debtBean = debtCalculationService.calculationDebt(saveBean.getNeedDealAcctList());

            // 7.将这些交易账户按照分配顺序真正分配，生成09交易账户，关闭交易账户  //添加代偿标识判断
            paymentAmt = this.repaymentAllocation(saveBean, loanMainInfo, loanAccountInfo, debtBean, paymentAmt, bussinessDate);

            // 8.生成09对应的gl流水
            List<TlGlInterfaceInfo> saveGlList = glInterfaceService.buildTlGlInterfaceInfoList(saveBean.getPaymentLogSaveList(), loanMainInfo, loanAccountInfo);
            saveBean.addSaveGlList(saveGlList);

            // 9.处理还款计划  //添加代偿标识判断
            this.dealPlan(saveBean, bussinessDate);

            // 10.处理订单  //添加代偿标识判断
            this.dealOrder(saveBean, loanMainInfo, loanAccountInfo);

            // 11.处理延滞
            this.dealDelq(saveBean, loanMainInfo, loanAccountInfo, delqParamBean, bussinessDate);

            // 12 判断是否记录五级分类变动
            if (TransBizConstant.LOAN_STATUS_7.equals(saveBean.getUpdateLoan().getAccountStatus()) ||
                    TransBizConstant.LOAN_STATUS_8.equals(saveBean.getUpdateLoan().getAccountStatus())) {
                String alterClass = saveBean.getUpdateLoan().getLoanClassify();
                if (StringUtils.isBlank(alterClass)) {
                    //如果为空表示五级分类不可逆，只是保存一条流水
                    alterClass = loanAccountInfo.getLoanClassify();
                }
                TlLoanClassifyChange tlLoanClassifyChange = tlLoanClassifyService.buildLoanClassifyChange(loanAccountInfo, bussinessDate, loanAccountInfo.getRemainPrincipal(), alterClass);
                saveBean.getSaveClassifyList().add(tlLoanClassifyChange);
            }

            saveBeanList.add(saveBean);
        }

        // 13.更新客户额度
        this.updateCustLmt(saveBeanList, loanMainInfoList);

        //更新合作方额度
        this.updatePartnerLimit(saveBeanList, loanMainInfoList, paymentLog.getSeqNo());

        // 14.保存所信息
        PaymentSaveBean bean = this.dealAllAccts(saveBeanList, tlTransactionInfo);
        this.saveAllAccts(bean);
    }

    /**
     * 账单分期分配
     *
     * @param tlLoanStagingMappingList 账单分期关联表
     * @param bussinessDate            业务日期
     * @throws TxnException 自定义异常
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void dealLoanStaging(List<TlLoanStagingMapping> tlLoanStagingMappingList, Date bussinessDate) throws TxnException {
        logger.info("账单分期分配开始 分期订单号:{}", tlLoanStagingMappingList.get(0).getStagingLoanId());
        List<PaymentSaveBean> saveBeanList = new ArrayList<>();
        /* 还款额 */
        BigDecimal payAmt = tlLoanStagingMappingList.get(0).getLoanAmount();

        List<AmLoanMainInfo> loanList = new ArrayList<>();
        //查询订单信息
        for (TlLoanStagingMapping mapping : tlLoanStagingMappingList) {
            logger.info("账单分期关联，mapping:{}", mapping);
            AmLoanMainInfo loanMainInfo = extAmLoanMainInfoMapper.selectByLoanIdAndCusId(mapping.getOriginalLoanId(),
                    mapping.getCustomerId(), mapping.getTenantId());
            logger.info("订单，loanMainInfo:{}", loanMainInfo);
            loanList.add(loanMainInfo);
        }
        //生成08交易账户
        TlPaymentAllocationLog totalPaymentLog = tlPaymentAllocationLogService.buildTlPaymentAllocationLog(tlLoanStagingMappingList.get(0),
                loanList.get(0), bussinessDate);
        // 2.生成还款交易流水
        TlTransactionInfo tlTransactionInfo = tlTransactionInfoService.buildTlTransactionInfo(totalPaymentLog);


        TlPaymentLog tlPaymentLog = new TlPaymentLog();
        tlPaymentLog.setSeqNo(SeqUtils.getSN());
        tlPaymentLog.setAmount(payAmt);
        tlPaymentLog.setPaymentType(TransBizConstant.REPAYMENT_TYPE_STAGING);
        for (AmLoanMainInfo loan : loanList) {
            PaymentSaveBean saveBean = this.dealPrePayNotSave(tlPaymentLog, loan, bussinessDate);
            BigDecimal realPayAmt = saveBean.getPaymentLogSaveList().stream().map(TlPaymentAllocationLog::getPostingAmount).reduce(BigDecimal.ZERO, BigDecimal::add);

            payAmt = payAmt.subtract(realPayAmt);
            tlPaymentLog.setAmount(payAmt);

            saveBean.getPaymentLogSaveList().forEach(log -> log.setOriginalAccountId(totalPaymentLog.getAccountId()));
            saveBean.getTransactionInfoList().removeIf(log -> TransBizConstant.TXN_CODE_PAYMENT_DETAIL.equals(log.getTxnCode()));
            saveBeanList.add(saveBean);
        }

        PaymentSaveBean bean = this.dealAllAccts(saveBeanList, tlTransactionInfo);
        bean.setSavaPaymentLog(totalPaymentLog);
        //保存入库
        this.saveAllAccts(bean);
    }

    /**
     * 获取需要处理的还款计划
     *
     * @param saveBean     保存本金利息罚息dto对象
     * @param loanMainInfo 订单信息
     * @param planStatus   还款计划状态
     */
    private void getNeedDealPlan(PaymentSaveBean saveBean, AmLoanMainInfo loanMainInfo, List<String> planStatus) throws TxnBizException {
        logger.info("获取需要处理的还款计划开始,planStatus:{}", planStatus);
        List<AmRepaymentPlanInfo> planInfoList = extAmRepaymentPlanInfoMapper.selectByLoanIdAndStatus(loanMainInfo.getLoanId()
                , planStatus, loanMainInfo.getCustomerId(), loanMainInfo.getTenantId());
        if (CollectionUtils.isEmpty(planInfoList)) {
            logger.info("loanId:{},无需要处理的还款计划", loanMainInfo.getLoanId());
            return;
        }
        //按期分组
        Map<Integer, AmRepaymentPlanInfo> planInfoMap = planInfoList.stream().collect(Collectors.toMap(AmRepaymentPlanInfo::getBillingTenor, Function.identity()));
        saveBean.setPlanInfoMap(planInfoMap);
        saveBean.getPlanInfoAllMap().put(loanMainInfo.getLoanId(), planInfoMap);
        logger.info("获取需要处理的还款计划结束,planInfoMap:{}", planInfoMap);
    }

    /**
     * 过滤还款计划
     *
     * @param saveBean      保存本金利息罚息dto对象
     * @param bussinessDate 业务日期
     */
    private void filterPlanMap(PaymentSaveBean saveBean, AmLoanMainInfo loanMainInfo, Date bussinessDate) {
        Map<Integer, AmRepaymentPlanInfo> planInfoMap = saveBean.getPlanInfoAllMap().get(loanMainInfo.getLoanId());
        if (null == planInfoMap) {
            return;
        }
        //筛选去除还款日大于当前业务日期的还款计划
        Predicate<AmRepaymentPlanInfo> filterPlan = plan -> plan.getPaymentDueDate().compareTo(bussinessDate) <= 0;
        Map<Integer, AmRepaymentPlanInfo> repaymentPlanInfoMap = saveBean.getPlanInfoMap().values().stream()
                .filter(filterPlan)//根据过滤条件筛选
                .collect(Collectors.toMap(AmRepaymentPlanInfo::getBillingTenor, Function.identity()));//按 k - tenor v-plan 的方式组成map
        saveBean.setPlanInfoMap(repaymentPlanInfoMap);
        Map<Integer, AmRepaymentPlanInfo> repaymentPlanInfoAllMap = saveBean.getPlanInfoAllMap().get(loanMainInfo.getLoanId()).values().stream()
                .filter(filterPlan)//根据过滤条件筛选
                .collect(Collectors.toMap(AmRepaymentPlanInfo::getBillingTenor, Function.identity()));//按 k - tenor v-plan 的方式组成map
        saveBean.getPlanInfoAllMap().put(loanMainInfo.getLoanId(),repaymentPlanInfoAllMap);
    }

    /**
     * 获取已抛帐本金交易，利息交易，罚息交易，罚息复利交易，分期费用交易账户
     *
     * @param saveBean     保存本金利息罚息dto对象
     * @param loanMainInfo 贷款信息
     * @param statusList   交易账户状态列表
     */
    private void getAlreadyCastAcct(PaymentSaveBean saveBean, AmLoanMainInfo loanMainInfo, List<String> statusList) throws TxnBizException {
        logger.info("获取本金交易，利息交易，分期费用账户开始,statusList:{}", statusList);
        String loanId = loanMainInfo.getLoanId();
        List<String> typeList = Arrays.asList(TransBizConstant.ACCOUNT_TYPE_03, TransBizConstant.ACCOUNT_TYPE_04,
                TransBizConstant.ACCOUNT_TYPE_06, TransBizConstant.ACCOUNT_TYPE_11, TransBizConstant.ACCOUNT_TYPE_12);
        String customerId = loanMainInfo.getCustomerId();
        String tenantId = loanMainInfo.getTenantId();

        Map<Integer, AmRepaymentPlanInfo> planInfoMap = saveBean.getPlanInfoAllMap().get(loanMainInfo.getLoanId());
        if (null == planInfoMap) {
            return;
        }

        List<AmAccountMainInfo> amAccountMainInfoList = extAmAccountMainInfoMapper.selectAccounts(loanId, typeList, customerId, tenantId, statusList);
        if (CollectionUtils.isEmpty(amAccountMainInfoList)) {
            logger.info(TransErrMsgConstant.NO_PAYMENT_ACCT);
            return;
        }
        logger.info("筛选交易账户");
        //只筛选拥有对应需要处理还款计划的交易账户，过滤掉不符合条件的，比如逾期还款的当期本金，费用账户
        Predicate<AmAccountMainInfo> filterAcct = acct -> null != planInfoMap.get(acct.getBillingTenor());
        amAccountMainInfoList.stream().filter(filterAcct).forEach(saveBean::addUpdateAcct);
        logger.info("获取本金交易，利息交易，分期费用账户结束,updateAcctList.size:{}", saveBean.getUpdateAcctList().size());
    }

    /**
     * 利息/罚息/罚息复利累计抛帐，获取利息/罚息/罚息复利交易，并关闭利息/罚息/罚息复利累计
     *
     * @param saveBean         保存本金利息罚息dto对象
     * @param loanMainInfo     贷款信息
     * @param loanAccountInfo  贷款账户信息
     * @param accuAcctTypeList 累计账户状态列表
     */
    private void castAccuAcct(PaymentSaveBean saveBean, AmLoanMainInfo loanMainInfo, AmLoanAccountInfo loanAccountInfo, List<String> accuAcctTypeList, Date businessDate) {
        //需要处理的还款计划
        Map<Integer, AmRepaymentPlanInfo> planInfoMap = saveBean.getPlanInfoAllMap().get(loanMainInfo.getLoanId());
        if (null == planInfoMap) {
            return;
        }

        String loanId = loanMainInfo.getLoanId();
        String customerId = loanMainInfo.getCustomerId();
        String tenantId = loanMainInfo.getTenantId();
        //获取利息/罚息/罚息复利累计
        List<AmInterestAccrualInfo> accuAcctList = extAmInterestAccrualInfoMapper.selectAllAccuAcct(
                loanId, customerId, tenantId, accuAcctTypeList, Collections.singletonList(TransBizConstant.ACCOUNT_STATUS_1));
        if (CollectionUtils.isEmpty(accuAcctList)) {
            return;
        }

        //筛选出符合的累计账户
        Predicate<AmInterestAccrualInfo> getAccuAcct = accuAcct -> null != planInfoMap.get(accuAcct.getBillingTenor());
        accuAcctList = accuAcctList.stream().filter(getAccuAcct).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(accuAcctList)) {
            return;
        }

        UncheckedConsumer<AmInterestAccrualInfo> castAccu = accuAcct -> {
            //通过订单，累计生成利息/罚息/罚息复利交易
            CastAccountTransactionBean intrCastAccrt = accountService.castInterestAccount(loanMainInfo, loanAccountInfo,
                    accuAcct, businessDate, false);
            logger.info("轮询当期利息／罚息累计，生成的交易账户intrAcct:{}", BeanUtils.toMap(intrCastAccrt.getAmAccountMainInfo()));
            //关闭原利息/罚息累计
            AmInterestAccrualInfo updateAccuAcct = new AmInterestAccrualInfo();
            updateAccuAcct.setId(accuAcct.getId());
            updateAccuAcct.setLoanId(accuAcct.getLoanId());
            updateAccuAcct.setTenantId(accuAcct.getTenantId());
            updateAccuAcct.setCustomerId(accuAcct.getCustomerId());
            updateAccuAcct.setAccountStatus(TransBizConstant.ACCOUNT_STATUS_8);
            updateAccuAcct.setBillingTenor(accuAcct.getBillingTenor());
            updateAccuAcct.setProvisionalAccrualInterest(accuAcct.getProvisionalAccrualInterest());
            updateAccuAcct.setProvisionalTaxAmt(accuAcct.getProvisionalTaxAmt());

            //添加代偿标识
            if (TransBizConstant.COMPENSATORY_FLAG_1.equals(loanAccountInfo.getCompensatoryFlag())) {
                updateAccuAcct.setCompensatoryFlag(TransBizConstant.COMPENSATORY_FLAG_1);
            }
            //保存生成的交易和需要更新的累计
            saveBean.addSaveAcct(intrCastAccrt.getAmAccountMainInfo());
            saveBean.addUpdateAccuAcct(updateAccuAcct);
            saveBean.addTransactionInfo(intrCastAccrt.getTlTransactionInfo());
            saveBean.addSaveGlList(intrCastAccrt.getTlGlInterfaceInfoList());
        };
        //轮询累计账户生成对应的交易账户
        accuAcctList.forEach(CollectorsUtils.tranToUncheck(castAccu));
    }

    /**
     * 未抛帐的本金抛帐，获取本金交易账户
     *
     * @param saveBean
     * @param loanMainInfo
     * @param loanAccountInfo
     * @param delqParamBean
     * @param bussinessDate
     */
    private void castNonCastPrinAcct(PaymentSaveBean saveBean, AmLoanMainInfo loanMainInfo, AmLoanAccountInfo loanAccountInfo, DelqParamBean delqParamBean,
                                     Date bussinessDate) {
        Map<Integer, AmRepaymentPlanInfo> planInfoMap = saveBean.getPlanInfoMap();
        //循环未抛帐的还款计划，生成对应的本金交易账户
        UncheckedBiConsumer<Integer, AmRepaymentPlanInfo> castPrinAcct = (tenor, plan) -> {
            if (TransBizConstant.LOAN_PLAN_STATUS_0.equals(plan.getPlanStatus())) {
                CastAccountTransactionBean intrCastAccrt = accountService.castPrincipalAccount(loanMainInfo, loanAccountInfo, plan, delqParamBean, bussinessDate);
                saveBean.addSaveAcct(intrCastAccrt.getAmAccountMainInfo());
                saveBean.addTransactionInfo(intrCastAccrt.getTlTransactionInfo());
                saveBean.addSaveGlList(intrCastAccrt.getTlGlInterfaceInfoList());
            }
        };
        planInfoMap.forEach(CollectorsUtils.tranToUncheck(castPrinAcct));
    }

    /**
     * 调整本金交易账户
     *
     * @param saveBean
     * @param loanMainInfo
     * @param loanAccountInfo
     * @param debtBean
     * @param delqParamBean
     * @param bussinessDate
     * @throws TxnBizException
     */
    private void adjustPrinAcct(PaymentSaveBean saveBean, AmLoanMainInfo loanMainInfo, AmLoanAccountInfo loanAccountInfo,
                                DebtBean debtBean, DelqParamBean delqParamBean, Date bussinessDate) throws TxnException {
        /*
         *     应还金额与还款金额进行比较。如果还款金额更大，则再生成一个当期的本金交易账户，入账金额为其差值。
         *     如果还款金额更小，则更新当期的本金交易账户的待调金额为其差值。通过实际还款本金得到还款后的剩余本金
         */

        List<AmAccountMainInfo> upDateAcctList = saveBean.getUpdateAcctList();
        //交易信息
        Map<Integer, AmRepaymentPlanInfo> planInfoMap = saveBean.getPlanInfoMap();
        //还款金额
        BigDecimal repayAmt = saveBean.getSavaPaymentLog().getPostingAmount();
        //已抛账户总欠款
        BigDecimal remainAmt = debtBean.getTotalAmount();
        //剩余未抛本金
        BigDecimal unCastPrin = loanAccountInfo.getRemainPrincipal().subtract(loanAccountInfo.getPrincipalBalance());
        //未还已抛未还利息，罚息，复利之和
        BigDecimal notPrinRemainAmt = remainAmt.subtract(loanAccountInfo.getPrincipalBalance());
        //还款后剩余金额
        BigDecimal afterRepayBal = repayAmt.subtract(remainAmt).subtract(unCastPrin);
        logger.info("提前还款处理本金交易账户，还款金额:{}，已抛账户总欠款:{}，剩余未抛本金:{},本次还款后的剩余本金:{}", repayAmt, remainAmt, unCastPrin, afterRepayBal);
        if (afterRepayBal.compareTo(BigDecimal.ZERO) >= 0) {
            //还款金额 >= 已抛账户总欠款+未抛本金 按提前结清处理，抛出剩余未抛本金
            this.castNonCastPrinAcct(saveBean, loanMainInfo, loanAccountInfo, delqParamBean, bussinessDate);
            //更新总欠款，使其完全分配
            debtBean.setTotalAmount(repayAmt);
        } else if (repayAmt.compareTo(remainAmt) > 0) {
            //还款金额 > 已抛账户总欠款 && 还款金额 < 已抛账户总欠款+未抛本金  多出的金额则再生成一个当期的本金交易账户 即溢还款本金交易账户
            //查询当期还款计划
            AmRepaymentPlanInfo castPlan = planInfoMap.get(loanAccountInfo.getCastTenor());
            if (null == castPlan) {
                //当期已结清
                castPlan = extAmRepaymentPlanInfoMapper.selectByLoanIdAndBillingTenor(loanAccountInfo.getLoanId(),
                        loanAccountInfo.getCustomerId(), loanAccountInfo.getTenantId(), loanAccountInfo.getCastTenor(), Collections.singletonList(TransBizConstant.LOAN_PLAN_STATUS_8));
            }
            AmRepaymentPlanInfo newCastPlan = new AmRepaymentPlanInfo();
            BeanMapping.copy(castPlan, newCastPlan);
            newCastPlan.setPrincipalAmount(repayAmt.subtract(remainAmt));
            CastAccountTransactionBean prinCastAcct = accountService.castPrincipalAccount(loanMainInfo, loanAccountInfo,
                    newCastPlan, delqParamBean, bussinessDate);
            logger.info("溢出本金交易账户prinAcct:{}", BeanUtils.toMap(prinCastAcct.getAmAccountMainInfo()));
            saveBean.addSaveAcct(prinCastAcct.getAmAccountMainInfo());
            saveBean.addTransactionInfo(prinCastAcct.getTlTransactionInfo());
            saveBean.addSaveGlList(prinCastAcct.getTlGlInterfaceInfoList());
            //更新总欠款，使其完全分配
            debtBean.setTotalAmount(repayAmt);
        } else {
            //还款金额 > 未还已抛未还利息，罚息，复利之和
            if (repayAmt.compareTo(notPrinRemainAmt) > 0
                    //还款日当天，不调整本金
                    && !bussinessDate.equals(loanAccountInfo.getNextPaymentDate())) {
                //还款金额 <= 总欠款
                //判断是否为当期本金
                BigDecimal subtractAmt = remainAmt.subtract(repayAmt);
                Predicate<AmAccountMainInfo> checkCastPrin = prinAcct -> loanAccountInfo.getCastTenor().equals(prinAcct.getBillingTenor())
                        && TransBizConstant.ACCOUNT_TYPE_06.equals(prinAcct.getAccountType());
                //更新当期的本金交易账户的贷调金额为 总欠款-还款金额
                upDateAcctList.stream()
                        .filter(checkCastPrin)
                        .forEach(
                                acct -> {
                                    acct.setCreditAdjustAmount(acct.getCreditAdjustAmount().add(subtractAmt));
                                    acct.setCurrBalance(acct.getCurrBalance().subtract(subtractAmt));
                                    logger.info("调整当期本金交易账户acct:{}", BeanUtils.toMap(acct));
                                });
                //已抛未还本金需要减去贷调金额
                loanAccountInfo.setPrincipalBalance(loanAccountInfo.getPrincipalBalance().subtract(subtractAmt));
                //已抛未还总金额需要减去贷调金额
                loanAccountInfo.setCurrBalance(loanAccountInfo.getCurrBalance().subtract(subtractAmt));
                //更新总欠款，使其完全分配
                debtBean.setTotalAmount(repayAmt);
            } else {
                //做当期部分还款
                planInfoMap.values().removeIf(plan -> plan.getBillingTenor() > loanAccountInfo.getCastTenor());
            }
        }
    }

    /**
     * 对需要分配的交易账户进行还款分配
     *
     * @param saveBean      保存本金利息罚息dto对象
     * @param loanMainInfo  贷款信息
     * @param debtBean      欠款信息
     * @param paymentAmt    还款额
     * @param bussinessDate 业务日期
     * @return 还款额
     * @throws TxnException 自定义异常
     */
    private BigDecimal repaymentAllocation(PaymentSaveBean saveBean, AmLoanMainInfo loanMainInfo,
                                           AmLoanAccountInfo loanAccountInfo, DebtBean debtBean,
                                           BigDecimal paymentAmt, Date bussinessDate) throws TxnBizException {
        //需要分配的交易账户
        List<AmAccountMainInfo> needDealAcctList = saveBean.getNeedDealAcctList();

        //08交易账户
        TlPaymentAllocationLog totalAllocationLog = saveBean.getSavaPaymentLog();
        //当前总欠款
        BigDecimal debtAmt = debtBean.getTotalAmount();
        //需要处理的还款计划
        Map<Integer, AmRepaymentPlanInfo> planMap = saveBean.getPlanInfoMap();

        //还款金额小于欠款，即部分还款，按还款顺序分配
        if (paymentAmt.compareTo(debtAmt) < 0) {
            //按照还款顺序进行排
            int maxDelqDays = loanAccountInfo.getDelqDays();//当前逾期天数
            needDealAcctList = this.sortByPaymentSequence(needDealAcctList, loanMainInfo, maxDelqDays);
        }

        //开始分配
        //原来需要更新交易账户
        List<AmAccountMainInfo> oriUpdateAcctList = saveBean.getUpdateAcctList();
        //原来需要新增交易账户
        List<AmAccountMainInfo> oriSaveAcctList = saveBean.getSaveAcctList();
        //需要更新本，利，罚，费交易账户
        List<AmAccountMainInfo> updateAcctList = new ArrayList<>();
        //需要新增本，利，罚，费交易账户
        List<AmAccountMainInfo> saveAcctList = new ArrayList<>();

        for (AmAccountMainInfo needDealAcct : needDealAcctList) {
            logger.info("开始分配交易账户orgiAcct:{},还款金额:{}", BeanUtils.toMap(needDealAcct), paymentAmt);
            //单笔欠款
            BigDecimal acctRemainAmnt = needDealAcct.getCurrBalance();
            logger.info("单笔欠款acctRemainAmnt:{}", acctRemainAmnt);
            //还款金额已分配完
            if (paymentAmt.compareTo(BigDecimal.ZERO) <= 0 && acctRemainAmnt.compareTo(BigDecimal.ZERO) > 0) {
                logger.info("还款金额小于0，结束分配");
                break;
            }

            //真正子交易还款金额
            BigDecimal realPaymentAmnt;
            //还款金额<=欠款金额,生成一笔还款子交易，金额为还款金额
            if (paymentAmt.compareTo(acctRemainAmnt) <= 0) {
                realPaymentAmnt = paymentAmt;
                logger.info("还款金额<=欠款金额，生成一笔还款子交易，金额为还款金额realPaymentAmnt:{}", realPaymentAmnt);
            } else {
                // 还款金额>欠款金额，生成一笔还款子交易，金额为欠款金额
                realPaymentAmnt = acctRemainAmnt;
                logger.info("还款金额>欠款金额，生成一笔还款子交易，金额为欠款金额realPaymentAmnt:{}", realPaymentAmnt);
            }
            paymentAmt = paymentAmt.subtract(realPaymentAmnt);

            //生成对应的09交易账户  添加代偿标识判断
            TlPaymentAllocationLog paymentLog = tlPaymentAllocationLogService.buildTlPaymentAllocationLog(totalAllocationLog,
                    needDealAcct, realPaymentAmnt);
            saveBean.addPaymentLog(paymentLog);


            //关闭原交易账户
            logger.info("关闭原交易账户");
            needDealAcct.setRepayAmount(needDealAcct.getRepayAmount().add(realPaymentAmnt));
            needDealAcct.setCurrBalance(needDealAcct.getCurrBalance().subtract(realPaymentAmnt));
            needDealAcct.setLastPaymentTime(DateUtils.getBusinessTime(new Date(), bussinessDate));
            if (TransBizConstant.COMPENSATORY_FLAG_1.equals(loanAccountInfo.getCompensatoryFlag())) {
                needDealAcct.setCompensatoryFlag(TransBizConstant.COMPENSATORY_FLAG_1);
                needDealAcct.setCompensatoryDays(loanAccountInfo.getDelqDays());
            }

            //如果原账户金额已还完
            if (needDealAcct.getCurrBalance().compareTo(BigDecimal.ZERO) <= 0) {
                if (TransBizConstant.REPAYMENT_TYPE_PRETERMINTION.equals(totalAllocationLog.getPaymentType())) {
                    needDealAcct.setAccountStatus(TransBizConstant.ACCOUNT_STATUS_7);
                } else {
                    needDealAcct.setAccountStatus(TransBizConstant.ACCOUNT_STATUS_8);
                }
            }

            //轮询原更新队列，如果存在AccountId相同，则更新，否则应该新增
            Predicate<AmAccountMainInfo> isSameAcctId = acct -> needDealAcct.getAccountId().equals(acct.getAccountId());

            long count = oriUpdateAcctList.stream().filter(isSameAcctId).count();
            if (count > 0) {
                logger.info("需要更新orgiAcct:{}", BeanUtils.toMap(needDealAcct));
                updateAcctList.add(needDealAcct);
                oriUpdateAcctList.removeIf(isSameAcctId);
            } else {
                logger.info("需要新增orgiAcct:{}", BeanUtils.toMap(needDealAcct));
                saveAcctList.add(needDealAcct);
                oriSaveAcctList.removeIf(isSameAcctId);
            }
        }

        //重新设置需要保存的交易账户
        saveAcctList.addAll(oriSaveAcctList);
        saveBean.setSaveAcctList(saveAcctList);
        //重新设置需要更新的交易账户
        updateAcctList = this.copyUpdateAcctList(updateAcctList);
        saveBean.setUpdateAcctList(updateAcctList);

        List<AmAccountMainInfo> allAcctList = new ArrayList<>();
        allAcctList.addAll(saveAcctList);
        allAcctList.addAll(updateAcctList);
        allAcctList.addAll(oriUpdateAcctList);
        saveBean.setAllAcctList(allAcctList);

        return paymentAmt;
    }

    /**
     * 对需要分配的交易账户进行还款分配
     *
     * @param saveBean     保存本金利息罚息dto对象
     * @param loanMainInfo 贷款信息
     * @param debtBean     欠款信息
     * @param paymentAmt   还款额
     * @return 还款额
     * @throws TxnException 自定义异常
     */
    private List<TlPaymentAllocationLog> preRepaymentAllocation(PaymentSaveBean saveBean, AmLoanMainInfo loanMainInfo,
                                                                DebtBean debtBean, BigDecimal paymentAmt) throws TxnException {

        List<TlPaymentAllocationLog> tlPaymentAllocationLogs = new ArrayList<>();

        //需要分配的交易账户
        List<AmAccountMainInfo> needDealAcctList = saveBean.getNeedDealAcctList();

        //总欠款
        BigDecimal debtAmt = debtBean.getTotalAmount();
        //需要处理的还款计划
        Map<Integer, AmRepaymentPlanInfo> planMap = saveBean.getPlanInfoMap();

        //还款金额小于欠款，即部分还款，按还款顺序分配
        if (paymentAmt.compareTo(debtAmt) < 0) {
            //按照还款顺序进行排
            int maxDelqDays = planMap.values().stream()
                    .map(AmRepaymentPlanInfo::getDelqDays)//获取延滞天数
                    .filter(Objects::nonNull)//筛选延滞天数不为空
                    .max(Integer::compareTo)//得到最大值
                    .orElse(0);//如果都没有则为0
            needDealAcctList = this.sortByPaymentSequence(needDealAcctList, loanMainInfo, maxDelqDays);
        }


        for (AmAccountMainInfo needDealAcct : needDealAcctList) {
            logger.info("开始分配交易账户orgiAcct:{},还款金额:{}", BeanUtils.toMap(needDealAcct), paymentAmt);
            //单笔欠款
            BigDecimal acctRemainAmnt = needDealAcct.getCurrBalance();
            logger.info("单笔欠款acctRemainAmnt:{}", acctRemainAmnt);
            //还款金额已分配完
            if (paymentAmt.compareTo(BigDecimal.ZERO) <= 0 && acctRemainAmnt.compareTo(BigDecimal.ZERO) > 0) {
                logger.info("还款金额小于0，结束分配");
                break;
            }

            //真正子交易还款金额
            BigDecimal realPaymentAmnt;
            //还款金额<=欠款金额,生成一笔还款子交易，金额为还款金额
            if (paymentAmt.compareTo(acctRemainAmnt) <= 0) {
                realPaymentAmnt = paymentAmt;
            } else {
                // 还款金额>欠款金额，生成一笔还款子交易，金额为欠款金额
                realPaymentAmnt = acctRemainAmnt;
            }
            paymentAmt = paymentAmt.subtract(realPaymentAmnt);

            //生成对应的09交易账户  添加代偿标识判断
            TlPaymentAllocationLog paymentLog = new TlPaymentAllocationLog();
            paymentLog.setConnectAccountType(needDealAcct.getAccountType());
            paymentLog.setPostingAmount(realPaymentAmnt);
            tlPaymentAllocationLogs.add(paymentLog);
        }
        return tlPaymentAllocationLogs;
    }

    /**
     * 生成需要更新的交易账户
     *
     * @param orgiAcctList 原始账户列表
     * @return 交易账户list
     */
    private List<AmAccountMainInfo> copyUpdateAcctList(List<AmAccountMainInfo> orgiAcctList) {
        UnaryOperator<AmAccountMainInfo> copyAcct = orgiAcct -> {
            AmAccountMainInfo acct = new AmAccountMainInfo();
            acct.setId(orgiAcct.getId());
            acct.setLoanId(orgiAcct.getLoanId());
            acct.setTenantId(orgiAcct.getTenantId());
            acct.setCustomerId(orgiAcct.getCustomerId());
            acct.setBillingTenor(orgiAcct.getBillingTenor());
            acct.setRepayAmount(orgiAcct.getRepayAmount());
            acct.setCreditAdjustAmount(orgiAcct.getCreditAdjustAmount());
            acct.setCurrBalance(orgiAcct.getCurrBalance());
            acct.setAccountStatus(orgiAcct.getAccountStatus());
            acct.setLastPaymentTime(orgiAcct.getLastPaymentTime());
            acct.setCompensatoryFlag(orgiAcct.getCompensatoryFlag());
            acct.setCompensatoryDays(orgiAcct.getCompensatoryDays());
            return acct;
        };
        return orgiAcctList.stream().map(copyAcct).collect(Collectors.toList());
    }

    /**
     * 按还款顺序对交易账户进行排序
     *
     * @param amAccountMainInfoList 账户信息list
     * @param loanMainInfo          贷款信息
     * @param maxDelqDays           最大延滞天数
     * @return 账户列表
     * @throws TxnException 自定义异常
     */
    private List<AmAccountMainInfo> sortByPaymentSequence(List<AmAccountMainInfo> amAccountMainInfoList, AmLoanMainInfo loanMainInfo, int maxDelqDays) throws TxnBizException {
        String paymentSequenceTableId = loanMainInfo.getPaymentSequenceTableId();
        String tenanId = loanMainInfo.getTenantId();
        PaymentSequenceTableRes prPaymentSequenceTable = paymentSequenceTableService.getByTenIdAndPaymentSequenceId(tenanId, paymentSequenceTableId);
        if (null == prPaymentSequenceTable || CollectionUtils.isEmpty(prPaymentSequenceTable.getControlReqList())) {
            logger.error(TransErrMsgConstant.NO_PAYMENT_SEQUENCE_PARAM);
            throw new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
        }

        List<PrPaymentSequenceFactorTable> prPaymentSequenceFactorTableList = prPaymentSequenceTable.getControlReqList().stream()
                .filter(factorTable -> factorTable.getDelqDaysBegin() <= maxDelqDays && factorTable.getDelqDaysEnd() > maxDelqDays)//筛选符合延滞天数的规则
                .collect(Collectors.toList());

        if (CollectionUtils.isEmpty(prPaymentSequenceFactorTableList)) {
            logger.error(TransErrMsgConstant.NO_PAYMENT_SEQUENCE_PARAM);
            throw new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
        }


        /*
         * 构建辅助排序队列
         */
        Function<AmAccountMainInfo, AmAccountMainInfoSortBean> createSortBean = amAccountMainInfo -> {
            AmAccountMainInfoSortBean sortBean = new AmAccountMainInfoSortBean();
            sortBean.setAcct(amAccountMainInfo);
            PrPaymentSequenceFactorTable acctPaymentSeq = prPaymentSequenceFactorTableList.stream()
                    //判断还款顺序账户类型是否和需要排序的账户类型一致
                    .filter(prPaymentSeq -> prPaymentSeq.getAccountType().equals(amAccountMainInfo.getAccountType()))
                    .findFirst()
                    .orElseThrow(() -> {
                        logger.error(TransErrMsgConstant.PAYMENT_SEQUENCE_PARAM_ERROR);
                        return new RuntimeException(TxnRespCode.ERROR.getUserTips());
                    });
            sortBean.setPriority(acctPaymentSeq.getPriority());
            return sortBean;
        };
        List<AmAccountMainInfoSortBean> sortDtoList = amAccountMainInfoList.stream().map(createSortBean).collect(Collectors.toList());

        /*
         * 对辅助排序队列进行排序
         */
        //辅助排序结果队列
        List<AmAccountMainInfoSortBean> finnalSortDtoList = new ArrayList<>();
        //冲减顺序
        String precedence = prPaymentSequenceTable.getPrecedence();
        //按期排序函数
        ToIntFunction<AmAccountMainInfoSortBean> comparByTenor = p -> p.getAcct().getBillingTenor();
        //按生成时间排序函数
        ToDateFunction<AmAccountMainInfoSortBean> comparByPostingDate = p -> p.getAcct().getPostingDate();
        if (TransBizConstant.PAYMENT_PRECEDENCE_0.equals(precedence)) {
            logger.info("冲帐顺序为横冲");
            //横冲，先按期从小到大排序，再按优先级从大到小排序,最后按业务时间从小到大排序
            finnalSortDtoList = sortDtoList.stream().sorted(
                    Comparator.comparingInt(comparByTenor)
                            .thenComparing(AmAccountMainInfoSortBean::getPriority, Comparator.reverseOrder())
                            .thenComparing(comparByPostingDate))
                    .collect(Collectors.toList());
        } else if (TransBizConstant.PAYMENT_PRECEDENCE_1.equals(precedence)) {
            logger.info("冲帐顺序为竖冲");
            //竖冲，先按优先级从大到小排序，再按期小到大排序,最后按业务时间从小到大排序
            finnalSortDtoList = sortDtoList.stream().sorted(
                    Comparator.comparing(AmAccountMainInfoSortBean::getPriority, Comparator.reverseOrder())
                            .thenComparingInt(comparByTenor)
                            .thenComparing(comparByPostingDate))
                    .collect(Collectors.toList());
        }

        //构建返回结果队列
        return finnalSortDtoList.stream().map(AmAccountMainInfoSortBean::getAcct).collect(Collectors.toList());
    }

    /**
     * 账单还款还款分配
     *
     * @param saveBean
     * @param loanMainInfoList
     * @param loanAccountInfoList
     * @param totalDebt
     * @param paymentAmt
     * @param bussinessDate
     */
    private void repaymentBillAllocation(PaymentSaveBean saveBean, List<AmLoanMainInfo> loanMainInfoList, List<AmLoanAccountInfo> loanAccountInfoList
            , BigDecimal totalDebt, BigDecimal paymentAmt, Date bussinessDate) throws TxnBizException {
        //需要分配的交易账户
        List<AmAccountMainInfo> needDealAcctList = saveBean.getNeedDealAcctList();

        //08交易账户
        TlPaymentAllocationLog totalAllocationLog = saveBean.getSavaPaymentLog();
        //需要处理的还款计划
        Map<String, Map<Integer, AmRepaymentPlanInfo>> planAllMap = saveBean.getPlanInfoAllMap();

        //还款金额小于欠款，即部分还款，按还款顺序分配
        if (paymentAmt.compareTo(totalDebt) < 0) {
            needDealAcctList = this.sortByPaymentSequenceForBill(needDealAcctList, loanMainInfoList, planAllMap);
        }

        //开始分配
        //原来需要更新交易账户
        List<AmAccountMainInfo> oriUpdateAcctList = saveBean.getUpdateAcctList();
        //原来需要新增交易账户
        List<AmAccountMainInfo> oriSaveAcctList = saveBean.getSaveAcctList();
        //需要更新本，利，罚，费交易账户
        List<AmAccountMainInfo> updateAcctList = new ArrayList<>();
        //需要新增本，利，罚，费交易账户
        List<AmAccountMainInfo> saveAcctList = new ArrayList<>();

        for (AmAccountMainInfo needDealAcct : needDealAcctList) {
            logger.info("开始分配交易账户orgiAcct:{},还款金额:{}", BeanUtils.toMap(needDealAcct), paymentAmt);
            //单笔欠款
            BigDecimal acctRemainAmnt = needDealAcct.getCurrBalance();
            logger.info("单笔欠款acctRemainAmnt:{}", acctRemainAmnt);
            //还款金额已分配完
            if (paymentAmt.compareTo(BigDecimal.ZERO) <= 0 && acctRemainAmnt.compareTo(BigDecimal.ZERO) > 0) {
                logger.info("还款金额小于0，结束分配");
                break;
            }

            //真正子交易还款金额
            BigDecimal realPaymentAmnt;
            //还款金额<=欠款金额,生成一笔还款子交易，金额为还款金额
            if (paymentAmt.compareTo(acctRemainAmnt) <= 0) {
                realPaymentAmnt = paymentAmt;
                logger.info("还款金额<=欠款金额，生成一笔还款子交易，金额为还款金额realPaymentAmnt:{}", realPaymentAmnt);
            } else {
                // 还款金额>欠款金额，生成一笔还款子交易，金额为欠款金额
                realPaymentAmnt = acctRemainAmnt;
                logger.info("还款金额>欠款金额，生成一笔还款子交易，金额为欠款金额realPaymentAmnt:{}", realPaymentAmnt);
            }
            paymentAmt = paymentAmt.subtract(realPaymentAmnt);

            AmLoanAccountInfo loanAccountInfo = loanAccountInfoList.stream().filter(loan -> loan.getLoanId().equals(needDealAcct.getLoanId())).findFirst().get();
            //生成对应的09交易账户  添加代偿标识判断
            TlPaymentAllocationLog paymentLog = tlPaymentAllocationLogService.buildTlPaymentAllocationLog(totalAllocationLog,
                    needDealAcct, realPaymentAmnt);
            saveBean.addPaymentLog(paymentLog);


            //关闭原交易账户
            logger.info("关闭原交易账户");
            needDealAcct.setRepayAmount(needDealAcct.getRepayAmount().add(realPaymentAmnt));
            needDealAcct.setCurrBalance(needDealAcct.getCurrBalance().subtract(realPaymentAmnt));
            needDealAcct.setLastPaymentTime(DateUtils.getBusinessTime(new Date(), bussinessDate));
            if (TransBizConstant.COMPENSATORY_FLAG_1.equals(loanAccountInfo.getCompensatoryFlag())) {
                needDealAcct.setCompensatoryFlag(TransBizConstant.COMPENSATORY_FLAG_1);
                if (needDealAcct.getCompensatoryDays() == null) {
                    needDealAcct.setCompensatoryDays(0);
                } else {
                    int cmpsNumber = needDealAcct.getCompensatoryDays() + 1;
                    needDealAcct.setCompensatoryDays(cmpsNumber);
                }
            }

            //如果原账户金额已还完
            if (needDealAcct.getCurrBalance().compareTo(BigDecimal.ZERO) <= 0) {
                needDealAcct.setAccountStatus(TransBizConstant.ACCOUNT_STATUS_8);
            }

            //轮询原更新队列，如果存在AccountId相同，则更新，否则应该新增
            Predicate<AmAccountMainInfo> isSameAcctId = acct -> needDealAcct.getAccountId().equals(acct.getAccountId());

            long count = oriUpdateAcctList.stream().filter(isSameAcctId).count();
            if (count > 0) {
                logger.info("需要更新orgiAcct:{}", BeanUtils.toMap(needDealAcct));
                updateAcctList.add(needDealAcct);
                oriUpdateAcctList.removeIf(isSameAcctId);
            } else {
                logger.info("需要新增orgiAcct:{}", BeanUtils.toMap(needDealAcct));
                saveAcctList.add(needDealAcct);
                oriSaveAcctList.removeIf(isSameAcctId);
            }
        }

        //重新设置需要保存的交易账户
        saveAcctList.addAll(oriSaveAcctList);
        saveBean.setSaveAcctList(saveAcctList);
        //重新设置需要更新的交易账户
        updateAcctList = this.copyUpdateAcctList(updateAcctList);
        saveBean.setUpdateAcctList(updateAcctList);

        List<AmAccountMainInfo> allAcctList = new ArrayList<>();
        allAcctList.addAll(saveAcctList);
        allAcctList.addAll(updateAcctList);
        allAcctList.addAll(oriUpdateAcctList);
        saveBean.setAllAcctList(allAcctList);
    }

    /**
     * 账单提前还款还款分配
     *
     * @param saveBean
     * @param loanMainInfoList
     * @param loanAccountInfoList
     * @param totalDebt
     * @param paymentAmt
     * @param bussinessDate
     */
    private void repaymentBillPreAllocation(PaymentSaveBean saveBean, List<AmLoanMainInfo> loanMainInfoList, List<AmLoanAccountInfo> loanAccountInfoList
            , BigDecimal totalDebt, BigDecimal paymentAmt, Date bussinessDate) throws TxnBizException {
        //需要分配的交易账户
        List<AmAccountMainInfo> needDealAcctList = saveBean.getNeedDealAcctList();

        //08交易账户
        TlPaymentAllocationLog totalAllocationLog = saveBean.getSavaPaymentLog();
        //需要处理的还款计划
        Map<String, Map<Integer, AmRepaymentPlanInfo>> planAllMap = saveBean.getPlanInfoAllMap();

        //还款金额小于欠款，即部分还款，按还款顺序分配
        if (paymentAmt.compareTo(totalDebt) < 0) {
            needDealAcctList = this.sortByPaymentSequenceForBill(needDealAcctList, loanMainInfoList, planAllMap);
        }

        //开始分配
        //原来需要更新交易账户
        List<AmAccountMainInfo> oriUpdateAcctList = saveBean.getUpdateAcctList();
        //原来需要新增交易账户
        List<AmAccountMainInfo> oriSaveAcctList = saveBean.getSaveAcctList();
        //需要更新本，利，罚，费交易账户
        List<AmAccountMainInfo> updateAcctList = new ArrayList<>();
        //需要新增本，利，罚，费交易账户
        List<AmAccountMainInfo> saveAcctList = new ArrayList<>();
        //未分配的本金交易账户Id
        List<String> notDealAcctIdList = new ArrayList<>();

        for (AmAccountMainInfo needDealAcct : needDealAcctList) {
            logger.info("开始分配交易账户orgiAcct:{},还款金额:{}", BeanUtils.toMap(needDealAcct), paymentAmt);
            //单笔欠款
            BigDecimal acctRemainAmnt = needDealAcct.getCurrBalance();
            logger.info("单笔欠款acctRemainAmnt:{}", acctRemainAmnt);
            //还款金额已分配完
            if (paymentAmt.compareTo(BigDecimal.ZERO) <= 0 && acctRemainAmnt.compareTo(BigDecimal.ZERO) > 0) {
                logger.info("还款金额小于0，结束分配");
                if (TransBizConstant.ACCOUNT_TYPE_06.equals(needDealAcct.getAccountType())) {
                    logger.info("本金交易账户{}未分配", needDealAcct.getAccountId());
                    notDealAcctIdList.add(needDealAcct.getAccountId());
                }
                continue;
            }

            //真正子交易还款金额
            BigDecimal realPaymentAmnt;
            //还款金额<欠款金额,生成一笔还款子交易，金额为还款金额
            if (paymentAmt.compareTo(acctRemainAmnt) < 0) {
                realPaymentAmnt = paymentAmt;
                logger.info("还款金额<=欠款金额，生成一笔还款子交易，金额为还款金额realPaymentAmnt:{}", realPaymentAmnt);
                //如果本金交易账户,欠款>还款额，需要对本金交易账户进行调整
                if (TransBizConstant.ACCOUNT_TYPE_06.equals(needDealAcct.getAccountType())) {
                    //贷调金额为未还完的本金余额
                    BigDecimal adjustAmt = acctRemainAmnt.subtract(paymentAmt);
                    needDealAcct.setCreditAdjustAmount(needDealAcct.getCreditAdjustAmount().add(adjustAmt));
                    needDealAcct.setCurrBalance(needDealAcct.getCurrBalance().subtract(adjustAmt));
                    AmLoanAccountInfo loanAccountInfo = loanAccountInfoList.stream().filter(loan -> needDealAcct.getLoanId().equals(loan.getLoanId())).findFirst().get();
                    //已抛未还本金需要减去贷调金额
                    loanAccountInfo.setPrincipalBalance(loanAccountInfo.getPrincipalBalance().subtract(adjustAmt));
                    //已抛未还总金额需要减去贷调金额
                    loanAccountInfo.setCurrBalance(loanAccountInfo.getCurrBalance().subtract(adjustAmt));
                }
            } else {
                // 还款金额>=欠款金额，生成一笔还款子交易，金额为欠款金额
                realPaymentAmnt = acctRemainAmnt;
                logger.info("还款金额>欠款金额，生成一笔还款子交易，金额为欠款金额realPaymentAmnt:{}", realPaymentAmnt);
            }
            paymentAmt = paymentAmt.subtract(realPaymentAmnt);

            AmLoanAccountInfo loanAccountInfo = loanAccountInfoList.stream().filter(loan -> loan.getLoanId().equals(needDealAcct.getLoanId())).findFirst().get();
            //生成对应的09交易账户  添加代偿标识判断
            TlPaymentAllocationLog paymentLog = tlPaymentAllocationLogService.buildTlPaymentAllocationLog(totalAllocationLog,
                    needDealAcct, realPaymentAmnt);
            saveBean.addPaymentLog(paymentLog);


            //关闭原交易账户
            logger.info("关闭原交易账户");
            needDealAcct.setRepayAmount(needDealAcct.getRepayAmount().add(realPaymentAmnt));
            needDealAcct.setCurrBalance(needDealAcct.getCurrBalance().subtract(realPaymentAmnt));
            needDealAcct.setLastPaymentTime(DateUtils.getBusinessTime(new Date(), bussinessDate));
            if (TransBizConstant.COMPENSATORY_FLAG_1.equals(loanAccountInfo.getCompensatoryFlag())) {
                needDealAcct.setCompensatoryFlag(TransBizConstant.COMPENSATORY_FLAG_1);
                if (needDealAcct.getCompensatoryDays() == null) {
                    needDealAcct.setCompensatoryDays(0);
                } else {
                    int cmpsNumber = needDealAcct.getCompensatoryDays() + 1;
                    needDealAcct.setCompensatoryDays(cmpsNumber);
                }
            }

            //如果原账户金额已还完
            if (needDealAcct.getCurrBalance().compareTo(BigDecimal.ZERO) <= 0) {
                needDealAcct.setAccountStatus(TransBizConstant.ACCOUNT_STATUS_8);
            }

            //轮询原更新队列，如果存在AccountId相同，则更新，否则应该新增
            Predicate<AmAccountMainInfo> isSameAcctId = acct -> needDealAcct.getAccountId().equals(acct.getAccountId());

            long count = oriUpdateAcctList.stream().filter(isSameAcctId).count();
            if (count > 0) {
                logger.info("需要更新orgiAcct:{}", BeanUtils.toMap(needDealAcct));
                updateAcctList.add(needDealAcct);
                oriUpdateAcctList.removeIf(isSameAcctId);
            } else {
                logger.info("需要新增orgiAcct:{}", BeanUtils.toMap(needDealAcct));
                saveAcctList.add(needDealAcct);
                oriSaveAcctList.removeIf(isSameAcctId);
            }
        }

        //假如新生成的本金交易账户未分配，则不处理
        if (CollectionUtils.isNotEmpty(notDealAcctIdList)) {
            Predicate<AmAccountMainInfo> notDealPrinAcctId = acct -> notDealAcctIdList.contains(acct.getAccountId());
            oriSaveAcctList.removeIf(notDealPrinAcctId);
        }

        //重新设置需要保存的交易账户
        saveAcctList.addAll(oriSaveAcctList);
        saveBean.setSaveAcctList(saveAcctList);
        //重新设置需要更新的交易账户
        updateAcctList = this.copyUpdateAcctList(updateAcctList);
        saveBean.setUpdateAcctList(updateAcctList);

        List<AmAccountMainInfo> allAcctList = new ArrayList<>();
        allAcctList.addAll(saveAcctList);
        allAcctList.addAll(updateAcctList);
        allAcctList.addAll(oriUpdateAcctList);
        saveBean.setAllAcctList(allAcctList);
    }

    /**
     * 按还款顺序对交易账户进行排序(针对账单还款)
     *
     * @param amAccountMainInfoList
     * @param loanMainInfoList
     * @param planAllMap
     * @return
     */
    private List<AmAccountMainInfo> sortByPaymentSequenceForBill(List<AmAccountMainInfo> amAccountMainInfoList, List<AmLoanMainInfo> loanMainInfoList,
                                                                 Map<String, Map<Integer, AmRepaymentPlanInfo>> planAllMap) throws TxnBizException {

        String paymentSequenceTableId = loanMainInfoList.get(0).getPaymentSequenceTableId();
        String tenanId = loanMainInfoList.get(0).getTenantId();
        PaymentSequenceTableRes prPaymentSequenceTable = paymentSequenceTableService.getByTenIdAndPaymentSequenceId(tenanId, paymentSequenceTableId);
        if (null == prPaymentSequenceTable || CollectionUtils.isEmpty(prPaymentSequenceTable.getControlReqList())) {
            logger.error(TransErrMsgConstant.NO_PAYMENT_SEQUENCE_PARAM);
            throw new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
        }

        Map<String, List<PrPaymentSequenceFactorTable>> prPaymentSequenceFactorTableListMap = new HashMap<>();
        planAllMap.forEach(
                (loanId, planMap) -> {
                    //按照还款顺序进行排序
                    int maxDelqDays = planMap.values().stream()
                            .map(AmRepaymentPlanInfo::getDelqDays)//获取延滞天数
                            .filter(Objects::nonNull)//筛选延滞天数不为空
                            .max(Integer::compareTo)//得到最大值
                            .orElse(0);//如果都没有则为0
                    List<PrPaymentSequenceFactorTable> prPaymentSequenceFactorTableList = prPaymentSequenceTable.getControlReqList().stream()
                            .filter(factorTable -> factorTable.getDelqDaysBegin() <= maxDelqDays && factorTable.getDelqDaysEnd() > maxDelqDays)//筛选符合延滞天数的规则
                            .collect(Collectors.toList());

                    if (CollectionUtils.isEmpty(prPaymentSequenceFactorTableList)) {
                        throw new RuntimeException(TransErrMsgConstant.NO_PAYMENT_SEQUENCE_PARAM);
                    }
                    prPaymentSequenceFactorTableListMap.put(loanId, prPaymentSequenceFactorTableList);
                }
        );

        /*
         * 构建辅助排序队列
         */
        Function<AmAccountMainInfo, AmAccountMainInfoSortBean> createSortBean = amAccountMainInfo -> {
            AmAccountMainInfoSortBean sortBean = new AmAccountMainInfoSortBean();
            sortBean.setAcct(amAccountMainInfo);
            Date loanPostingDate = loanMainInfoList.stream().filter(loan -> amAccountMainInfo.getLoanId().equals(loan.getLoanId())).findFirst().get().getPostingDate();
            sortBean.setLoanPostingDate(loanPostingDate);
            PrPaymentSequenceFactorTable acctPaymentSeq = prPaymentSequenceFactorTableListMap.get(amAccountMainInfo.getLoanId()).stream()
                    //判断还款顺序账户类型是否和需要排序的账户类型一致
                    .filter(prPaymentSeq -> prPaymentSeq.getAccountType().equals(amAccountMainInfo.getAccountType()))
                    .findFirst()
                    .orElseThrow(() -> {
                        logger.error(TransErrMsgConstant.PAYMENT_SEQUENCE_PARAM_ERROR);
                        return new RuntimeException(TxnRespCode.ERROR.getUserTips());
                    });
            sortBean.setPriority(acctPaymentSeq.getPriority());
            sortBean.setPlanPaymentDueDate(planAllMap.get(amAccountMainInfo.getLoanId()).get(amAccountMainInfo.getBillingTenor()).getPaymentDueDate());
            return sortBean;
        };
        List<AmAccountMainInfoSortBean> sortDtoList = amAccountMainInfoList.stream().map(createSortBean).collect(Collectors.toList());


        /*
         * 对辅助排序队列进行排序
         */
        //辅助排序结果队列
        List<AmAccountMainInfoSortBean> finnalSortDtoList = new ArrayList<>();
        //冲减顺序
        String precedence = prPaymentSequenceTable.getPrecedence();
        //按生成时间排序函数
        ToDateFunction<AmAccountMainInfoSortBean> comparByPostingDate = p -> p.getAcct().getPostingDate();
        if (TransBizConstant.PAYMENT_PRECEDENCE_0.equals(precedence)) {
            logger.info("冲帐顺序为横冲");
            //横冲，先当期还款日从小到大排序，再按订单入账时间由小到大,再按优先级从大到小排序，最后按账户生成时间从小到大排序
            finnalSortDtoList = sortDtoList.stream().sorted(
                    Comparator.comparing(AmAccountMainInfoSortBean::getPlanPaymentDueDate)
                            .thenComparing(AmAccountMainInfoSortBean::getLoanPostingDate)
                            .thenComparing(AmAccountMainInfoSortBean::getPriority, Comparator.reverseOrder())
                            .thenComparing(comparByPostingDate))
                    .collect(Collectors.toList());
        } else if (TransBizConstant.PAYMENT_PRECEDENCE_1.equals(precedence)) {
            logger.info("冲帐顺序为竖冲");
            //竖冲，先按优先级从大到小排序，再当期还款日从小到大排序,再按订单入账时间由小到大,最后按业务时间从小到大排序
            finnalSortDtoList = sortDtoList.stream().sorted(
                    Comparator.comparing(AmAccountMainInfoSortBean::getPriority, Comparator.reverseOrder())
                            .thenComparing(AmAccountMainInfoSortBean::getPlanPaymentDueDate)
                            .thenComparing(AmAccountMainInfoSortBean::getLoanPostingDate)
                            .thenComparing(comparByPostingDate))
                    .collect(Collectors.toList());
        }

        //构建返回结果队列
        return finnalSortDtoList.stream().map(AmAccountMainInfoSortBean::getAcct).collect(Collectors.toList());
    }

    /**
     * 将原saveBean 按订单拆分生成新的saveBean
     *
     * @param saveBean
     * @param loanMainInfo
     * @return
     */
    private PaymentSaveBean afterBillAllocation(PaymentSaveBean saveBean, AmLoanMainInfo loanMainInfo) {
        String loanId = loanMainInfo.getLoanId();
        PaymentSaveBean saveBeanOrder = new PaymentSaveBean();
        saveBeanOrder.setSavaPaymentLog(saveBean.getSavaPaymentLog());
        saveBeanOrder.addPaymentLogList(saveBean.getPaymentLogSaveList().stream().filter(log -> loanId.equals(log.getLoanId())).collect(Collectors.toList()));
        saveBeanOrder.setPlanInfoMap(saveBean.getPlanInfoAllMap().get(loanId));
        saveBeanOrder.setSaveAcctList(saveBean.getSaveAcctList().stream().filter(acct -> loanId.equals(acct.getLoanId())).collect(Collectors.toList()));
        saveBeanOrder.setUpdateAcctList(saveBean.getUpdateAcctList().stream().filter(acct -> loanId.equals(acct.getLoanId())).collect(Collectors.toList()));
        saveBeanOrder.setAllAcctList(saveBean.getAllAcctList().stream().filter(acct -> loanId.equals(acct.getLoanId())).collect(Collectors.toList()));
        saveBeanOrder.setSaveAccuAcctList(saveBean.getSaveAccuAcctList().stream().filter(accuAcct -> loanId.equals(accuAcct.getLoanId())).collect(Collectors.toList()));
        saveBeanOrder.setUpdateAccuAcctList(saveBean.getUpdateAccuAcctList().stream().filter(accuAcct -> loanId.equals(accuAcct.getLoanId())).collect(Collectors.toList()));
        saveBeanOrder.setSaveGlList(saveBean.getSaveGlList().stream().filter(gl -> loanId.equals(gl.getLoanId())).collect(Collectors.toList()));
        saveBeanOrder.setTransactionInfoList(saveBean.getTransactionInfoList().stream().filter(tl -> loanId.equals(tl.getLoanId())).collect(Collectors.toList()));
        logger.info("saveBeanOrder:{}", BeanUtils.toMap(saveBeanOrder));
        return saveBeanOrder;
    }


    /**
     * 处理还款计划
     *
     * @param saveBean      保存本金利息罚息dto对象
     * @param bussinessDate 业务日期
     */
    private void dealPlan(PaymentSaveBean saveBean, Date bussinessDate) {
        Map<Integer, AmRepaymentPlanInfo> planInfoMap = saveBean.getPlanInfoMap();//所有处理的还款计划
        if (null == planInfoMap) {
            return;
        }

        logger.info("更新还款计划开始");
        List<TlPaymentAllocationLog> savePaymentLogList = saveBean.getPaymentLogSaveList();//09还款交易子账户
        List<AmAccountMainInfo> allAcctList = saveBean.getAllAcctList();//所有处理和未处理的交易账户
        TlPaymentAllocationLog totalPaymentLog = saveBean.getSavaPaymentLog();//08还款交易子账户
        //先按期分组
        Map<Integer, List<TlPaymentAllocationLog>> repayAmtListMap = savePaymentLogList.stream().collect(Collectors.groupingBy(TlPaymentAllocationLog::getBillingTenor));
        repayAmtListMap.forEach(
                //每期按关联账户类型分组求每期还款本金，还款利息，还款罚息，还款费用分别之和
                (tenor, paymentLogList) -> {
                    logger.info("更新还款计划,第{}期", tenor);
                    Map<String, BigDecimal> repayMap = paymentLogList.stream().collect(
                            Collectors.groupingBy(TlPaymentAllocationLog::getConnectAccountType, CollectorsUtils.summingBigDecimal(TlPaymentAllocationLog::getPostingAmount))
                    );
                    logger.info("更新还款计划,该期的欠款:{}", repayMap);
                    //更新每期还款计划状态，还款本金，还款利息，还款罚息
                    AmRepaymentPlanInfo plan = planInfoMap.get(tenor);
                    if (null == plan) {
                        return;
                    }
                    logger.info("更新还款计划,plan:{}", BeanUtils.toMap(plan));

                    AmRepaymentPlanInfo updatePlan = new AmRepaymentPlanInfo();
                    updatePlan.setId(plan.getId());
                    updatePlan.setCustomerId(plan.getCustomerId());
                    updatePlan.setTenantId(plan.getTenantId());
                    updatePlan.setBillingTenor(plan.getBillingTenor());

                    BigDecimal prinAmt = Optional.ofNullable(repayMap.get(TransBizConstant.ACCOUNT_TYPE_06)).orElse(BigDecimal.ZERO);
                    updatePlan.setRepayPrincipal(plan.getRepayPrincipal().add(prinAmt));
                    plan.setRepayPrincipal(plan.getRepayPrincipal().add(prinAmt));

                    BigDecimal intrAmt = Optional.ofNullable(repayMap.get(TransBizConstant.ACCOUNT_TYPE_04)).orElse(BigDecimal.ZERO);
                    updatePlan.setRepayInterest(plan.getRepayInterest().add(intrAmt));
                    plan.setRepayInterest(plan.getRepayInterest().add(intrAmt));

                    BigDecimal pentAmt = Optional.ofNullable(repayMap.get(TransBizConstant.ACCOUNT_TYPE_11)).orElse(BigDecimal.ZERO);
                    updatePlan.setRepayPenaltyInterest(plan.getRepayPenaltyInterest().add(pentAmt));
                    plan.setRepayPenaltyInterest(plan.getRepayPenaltyInterest().add(pentAmt));

                    BigDecimal compoundAmt = Optional.ofNullable(repayMap.get(TransBizConstant.ACCOUNT_TYPE_12)).orElse(BigDecimal.ZERO);
                    updatePlan.setRepayCompoundInterest(plan.getRepayCompoundInterest().add(compoundAmt));
                    plan.setRepayCompoundInterest(plan.getRepayCompoundInterest().add(compoundAmt));


                    //获取当期分期费用之和
                    BigDecimal repayFee = paymentLogList.stream()
                            .filter(log -> TransBizConstant.TXN_CODE_INST_PYMT_FEE.equals(log.getTxnCode()))
                            .map(TlPaymentAllocationLog::getPostingAmount)
                            .reduce(BigDecimal.ZERO, BigDecimal::add);
                    updatePlan.setRepayFee(plan.getRepayFee().add(repayFee));
                    plan.setRepayFee(plan.getRepayFee().add(repayFee));

                    //判断交易账户状态为正常或者逾期
                    Predicate<AmAccountMainInfo> acctNotClosed =
                            acct -> TransBizConstant.ACCOUNT_STATUS_1.equals(acct.getAccountStatus())
                                    || TransBizConstant.ACCOUNT_STATUS_2.equals(acct.getAccountStatus());
                    //判断是否为该期次账户
                    Predicate<AmAccountMainInfo> isSameTenor = acct -> tenor.equals(acct.getBillingTenor());
                    //轮询当期没有关闭的交易账户数量，如果当期没有正常或者逾期的交易账户，即当期如果交易已全部关闭
                    long acctCount = allAcctList.stream().filter(acctNotClosed.and(isSameTenor)).count();
                    //消费订单还款日当天退货可能出现无未结清交易账户，但当期还款计划未结清的情况
                    if (acctCount <= 0) {
                        if (TransBizConstant.REPAYMENT_TYPE_PRETERMINTION.equals(totalPaymentLog.getPaymentType())
                                || TransBizConstant.REPAYMENT_TYPE_PREPAY.equals(totalPaymentLog.getPaymentType())
                                || TransBizConstant.REPAYMENT_TYPE_CRT.equals(totalPaymentLog.getPaymentType())) {
                            //提前结清
                            plan.setPlanStatus(TransBizConstant.LOAN_PLAN_STATUS_7);//方便计算订单最大延滞天数
                            updatePlan.setPlanStatus(TransBizConstant.LOAN_PLAN_STATUS_7);
                            updatePlan.setOverDueAmount(BigDecimal.ZERO);
                            updatePlan.setPayoffDate(bussinessDate);
                        } else if (TransBizConstant.REPAYMENT_TYPE_STAGING.equals(totalPaymentLog.getPaymentType())) {
                            //账单分期
                            plan.setPlanStatus(TransBizConstant.LOAN_PLAN_STATUS_6);//方便计算订单最大延滞天数
                            updatePlan.setPlanStatus(TransBizConstant.LOAN_PLAN_STATUS_6);
                            updatePlan.setOverDueAmount(BigDecimal.ZERO);
                            updatePlan.setPayoffDate(bussinessDate);
                        } else {
                            //正常结清
                            plan.setPlanStatus(TransBizConstant.LOAN_PLAN_STATUS_8);//方便计算订单最大延滞天数
                            updatePlan.setPlanStatus(TransBizConstant.LOAN_PLAN_STATUS_8);
                            updatePlan.setOverDueAmount(BigDecimal.ZERO);
                            updatePlan.setPayoffDate(bussinessDate);
                        }
                    }
                    updatePlan.setTransactionDate(bussinessDate);
                    if (TransBizConstant.LOAN_PLAN_STATUS_2.equals(plan.getPlanStatus())) {
                        BigDecimal totalAmt = plan.getTotalAmount().subtract(plan.getRepayPrincipal()).subtract(plan.getRepayInterest())
                                .subtract(plan.getRepayFee());
                        plan.setOverDueAmount(totalAmt);
                        updatePlan.setOverDueAmount(totalAmt);
                    }
                    //添加代偿标识判断
                    if (TransBizConstant.COMPENSATORY_FLAG_1.equals(totalPaymentLog.getCompensatoryFlag())) {
                        updatePlan.setCompensateFlag(TransBizConstant.COMPENSATORY_FLAG_1);
                    }
                    logger.info("开始更新还款计划,更新后的还款计划updatePlan:{}", updatePlan);
                    planInfoMap.put(tenor, plan);
                    saveBean.setPlanInfoMap(planInfoMap);
                    saveBean.addUpdatePlan(updatePlan);
                }
        );
        logger.info("更新还款计划结束");
    }

    /**
     * 处理延滞
     *
     * @param saveBean
     */
    private void dealDelq(PaymentSaveBean saveBean, AmLoanMainInfo amLoanMainInfo, AmLoanAccountInfo amLoanAccountInfo,
                          DelqParamBean delqParamBean, Date bussinessDate) throws TxnBizException {
        if (null == saveBean.getPlanInfoMap()) {
            return;
        }

        //处理延滞交易账户
        this.updateDelqAcct(saveBean, amLoanMainInfo, amLoanAccountInfo, delqParamBean, bussinessDate);
        //还未关闭的订单写入会计待处理列表
        this.delqAccounting(saveBean, amLoanAccountInfo, bussinessDate);
    }

    /**
     * 处理延滞交易账户
     *
     * @param saveBean
     * @param amLoanMainInfo
     * @param amLoanAccountInfo
     * @param delqParamBean
     */
    private void updateDelqAcct(PaymentSaveBean saveBean, AmLoanMainInfo amLoanMainInfo, AmLoanAccountInfo amLoanAccountInfo, DelqParamBean delqParamBean, Date bussinessDate) {
        logger.info("处理延滞交易账户开始");
        String loanId = amLoanMainInfo.getLoanId();
        String customerId = amLoanMainInfo.getCustomerId();
        String tenantId = amLoanMainInfo.getTenantId();
        List<AmDelinquencyInfo> delqList = extAmDelinquencyInfoMapper.selectDelqInfoByLoanId(loanId, customerId, tenantId, Collections.singletonList(TransBizConstant.ACCOUNT_STATUS_1));

        PrPenaltyRateTable prPenaltyRateTable = prPenaltyRateTableService.findByPenaltyId(amLoanMainInfo.getPenaltyTableId(), amLoanMainInfo.getOrganizationId(), amLoanMainInfo.getTenantId());
        UncheckedConsumer<AmRepaymentPlanInfo> dealDelq = plan -> delqList.stream()
                .filter(delq -> delq.getBillingTenor().equals(plan.getBillingTenor()))
                .findFirst()
                .ifPresent(
                        delq -> {
                            logger.info("处理延滞交易账户delq:{}", BeanUtils.toMap(delq));

                            if (TransBizConstant.LOAN_PLAN_STATUS_7.equals(plan.getPlanStatus()) ||
                                    TransBizConstant.LOAN_PLAN_STATUS_8.equals(plan.getPlanStatus())) {
                                AmDelinquencyInfo updateDelq = new AmDelinquencyInfo();
                                updateDelq.setId(delq.getId());
                                updateDelq.setCustomerId(delq.getCustomerId());
                                updateDelq.setTenantId(delq.getTenantId());
                                updateDelq.setAccountStatus(TransBizConstant.ACCOUNT_STATUS_8);
                                saveBean.addUpdateDelq(updateDelq);
                            }

                            if (!TransBizConstant.LOAN_PLAN_STATUS_2.equals(plan.getPlanStatus())) {
                                return;
                            }

                            AmDelinquencyInfo updateDelq = new AmDelinquencyInfo();
                            updateDelq.setId(delq.getId());
                            updateDelq.setCustomerId(delq.getCustomerId());
                            updateDelq.setTenantId(delq.getTenantId());
                            updateDelq.setTotalAmountDue(plan.getOverDueAmount());
                            logger.info("处理延滞交易账户updateDelq:{}", BeanUtils.toMap(updateDelq));
                            saveBean.addUpdateDelq(updateDelq);


                            if (!TransBizConstant.NO_FLAG.equals(amLoanAccountInfo.getChargeoffFlag())) {
                                return;
                            }

                            //未核销订单生成罚息复利累计账户
                            List<AmInterestAccrualInfo> orgAccuAcctList = saveBean.getUpdateAccuAcctList();
                            try {
                                List<AmInterestAccrualInfo> accuAcctList = accountService.castPenAndComAccrualAccount(plan, amLoanMainInfo, amLoanAccountInfo,
                                        prPenaltyRateTable, delqParamBean, bussinessDate, bussinessDate);
                                //新生成的罚息复利累计加上原有的临时计提罚息复利
                                accuAcctList.forEach(
                                        accuAcct -> orgAccuAcctList.stream().filter(orgAccuAcct -> accuAcct.getLoanId().equals(orgAccuAcct.getLoanId()))
                                                .filter(orgAccuAcct -> accuAcct.getBillingTenor().equals(orgAccuAcct.getBillingTenor()))
                                                .findFirst().ifPresent(orgAccuAcct -> {
                                                            accuAcct.setProvisionalAccrualInterest(accuAcct.getProvisionalAccrualInterest().add(orgAccuAcct.getProvisionalAccrualInterest()));
                                                            accuAcct.setProvisionalTaxAmt(accuAcct.getProvisionalTaxAmt().add(orgAccuAcct.getProvisionalTaxAmt()));
                                                        }
                                                )
                                );
                                saveBean.addSaveAccuAcctList(accuAcctList);
                            } catch (Exception e) {
                                throw new RuntimeException(e);
                            }
                        }
                );

        //循环处理每期延滞
        saveBean.getPlanInfoMap().values().forEach(CollectorsUtils.tranToUncheck(dealDelq));
    }

    /**
     * 记录会计状态转列待处理列表
     *
     * @param saveBean
     * @param amLoanAccountInfo
     * @param bussinessDate
     */
    private void delqAccounting(PaymentSaveBean saveBean, AmLoanAccountInfo amLoanAccountInfo, Date bussinessDate) throws TxnBizException {
        if (TransBizConstant.LOAN_STATUS_7.equals(amLoanAccountInfo.getAccountStatus())
                || TransBizConstant.LOAN_STATUS_8.equals(amLoanAccountInfo.getAccountStatus())) {
            return;
        }
        String loanId = amLoanAccountInfo.getLoanId();
        String customerId = amLoanAccountInfo.getCustomerId();
        String tenantId = amLoanAccountInfo.getTenantId();
        int count = extBtAccountingTransferListMapper.selectCountTransferList(loanId, customerId, tenantId, bussinessDate);
        if (count > 0) {
            return;
        }
        BtAccountingTransferList transferList = btAccountingTransferListService.build(amLoanAccountInfo, bussinessDate);
        saveBean.addTransferList(transferList);
        logger.info("记录会计状态转列待处理列表transferList:{}", BeanUtils.toMap(transferList));


    }

    /**
     * 处理提前还款之后的还款计划
     *
     * @param saveBean      保存本金利息罚息dto对象
     * @param loanMainInfo  贷款订单
     * @param debtBean      欠款信息
     * @param bussinessDate 业务日期
     */
    private void dealPrePayPlan(PaymentSaveBean saveBean, AmLoanMainInfo loanMainInfo, AmLoanAccountInfo loanAccountInfo,
                                DebtBean debtBean, PrProductTable prProductTable, Date bussinessDate) throws TxnException {
        logger.info("处理提前还款后的还款计划");
        //还款金额
        BigDecimal repayAmt = saveBean.getSavaPaymentLog().getPostingAmount();
        //还款利，罚，费,罚息复利总金额
        BigDecimal remainAmt = debtBean.getInterestAmount().add(debtBean.getPenaltyAmount()).add(debtBean.getFeeAmount()).add(debtBean.getCompountAmount());
        //还款本金
        BigDecimal prinRepayAmt = repayAmt.subtract(remainAmt);
        //剩余本金
        BigDecimal prinBalAmt = loanAccountInfo.getRemainPrincipal();
        logger.info("还款本金prinRepayAmt:{},订单剩余本金prinBalAmt:{}", prinRepayAmt, prinBalAmt);
        //如果还款本金>=剩余本金
        if (prinRepayAmt.compareTo(prinBalAmt) >= 0) {
            //提前结清
            this.dealPlan(saveBean, bussinessDate);
        } else if (prinRepayAmt.compareTo(BigDecimal.ZERO) <= 0) {
            //如果还款本金<=0 即只还部分利息，罚息，复利,对当期做部分还款
            this.dealPlan(saveBean, bussinessDate);

            //还款日当天不重新生成累计账户
            if (bussinessDate.equals(loanAccountInfo.getNextPaymentDate())) {
                return;
            }

            //重新生成当期的累计账户
            DelqParamBean delqParamBean = delqService.buildDqlqAccountBean(loanMainInfo.getTenantId(), loanMainInfo.getDelqTableId());
            AmRepaymentPlanInfo dealPlan = saveBean.getPlanInfoMap().get(loanAccountInfo.getCastTenor());
            //获取当期本金
            dealPlan.setIntrStartDate(bussinessDate);
            AmInterestAccrualInfo accuAcct = accountService.castInterestAccrualAccount(dealPlan, loanAccountInfo, loanMainInfo, prinBalAmt, delqParamBean, dealPlan.getInterestAmount().subtract(loanAccountInfo.getInterestBalance()));
            if (null != accuAcct) {
                saveBean.getSaveAccuAcctList().add(accuAcct);
            }
        } else {
            //还款后剩余本金
            BigDecimal afterPayPrinBalAmt = prinBalAmt.subtract(prinRepayAmt);
            logger.info("还款后剩余本金afterPayPrinBalAmt:{}", afterPayPrinBalAmt);

            if (bussinessDate.equals(loanAccountInfo.getNextPaymentDate())) {
                AmRepaymentPlanInfo onDuePlan = saveBean.getPlanInfoMap().get(loanAccountInfo.getCastTenor());
                //如果当期未结清
                if (null != onDuePlan) {
                    this.dealPlan(saveBean, bussinessDate);
                    //如果当期未还完则不重新生成还款计划
                    if (!Arrays.asList(TransBizConstant.LOAN_PLAN_STATUS_7, TransBizConstant.LOAN_PLAN_STATUS_8).contains(onDuePlan.getPlanStatus())) {
                        return;
                    }
                }

                //取消非当期还款计划
                int ondueTenor = loanAccountInfo.getCastTenor();
                int tenor = loanMainInfo.getTotalTenor() - ondueTenor;
                this.cancelBillPayPlan(saveBean, loanAccountInfo.getCastTenor());
                int castTenor = ondueTenor + 1;
                Date nextPaymentDate = DateUtils.addMonth(loanAccountInfo.getNextPaymentDate(), 1);
                //除当期外重新生成还款计划
                this.reBuildBillPrepayPlan(saveBean, loanMainInfo, loanAccountInfo, afterPayPrinBalAmt, bussinessDate, prProductTable, tenor, castTenor, nextPaymentDate);
                //还款日当天暂时不抛帐
                loanAccountInfo.setNextPaymentDate(bussinessDate);
                loanAccountInfo.setCastTenor(ondueTenor);
            } else {
                //提前部分还款
                this.cancelPlan(saveBean);
                this.reBuildPlan(saveBean, loanMainInfo, loanAccountInfo, afterPayPrinBalAmt, prProductTable, bussinessDate);
            }
        }
    }

    /**
     * 处理账单提前还当期之后的还款计划
     *
     * @param saveBean      保存本金利息罚息dto对象
     * @param loanMainInfo  贷款订单
     * @param bussinessDate 业务日期
     */
    private void dealBillPrepayCurrPlan(PaymentSaveBean saveBean, AmLoanMainInfo loanMainInfo, AmLoanAccountInfo loanAccountInfo, Date bussinessDate,
                                        PrProductTable prProductTable, DebtBean debtBean) throws TxnException {
        logger.info("处理账单提前还当期后的还款计划");
        //还款金额
        BigDecimal repayAmt = saveBean.getPaymentLogSaveList().stream()
                .map(TlPaymentAllocationLog::getPostingAmount).reduce(BigDecimal.ZERO, BigDecimal::add);

        //没有任何还款
        if (repayAmt.compareTo(BigDecimal.ZERO) <= 0) {
            return;
        }

        //还款本金
        BigDecimal prinRepayAmt = saveBean.getPaymentLogSaveList().stream()
                .filter(log -> TransBizConstant.ACCOUNT_TYPE_06.equals(log.getConnectAccountType()))
                .map(TlPaymentAllocationLog::getPostingAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        //当期欠款
        BigDecimal debtAmt = debtBean.getTotalAmount();
        //剩余未还本金
        BigDecimal prinBalAmt = loanAccountInfo.getRemainPrincipal();
        logger.info("还款金额repayAmt:{},当期欠款debtAmt:{}", repayAmt, debtAmt);
        //如果还款金额<欠款金额
        if (repayAmt.compareTo(debtAmt) < 0) {
            //之前还款的利息
            BigDecimal repayInterestAmtBefore = saveBean.getPlanInfoMap().get(loanAccountInfo.getCastTenor()).getRepayInterest();
            //当期部分还款
            this.dealPlan(saveBean, bussinessDate);
            //当期利息累计重新抛帐
            PrInterestTable interestTable = interestTableService.findInterestByInterestId(loanMainInfo.getInterestTableId(), loanMainInfo.getTenantId());
            if (TransBizConstant.START_DATE_FLAG_2.equals(interestTable.getStartDateFlag())) {
                return;
            }

            DelqParamBean delqParamBean = delqService.buildDqlqAccountBean(loanMainInfo.getTenantId(), loanMainInfo.getDelqTableId());
            AmRepaymentPlanInfo dealPlan = saveBean.getPlanInfoMap().get(loanAccountInfo.getCastTenor());
            //剩余本金
            BigDecimal remainPrinAmt = loanAccountInfo.getRemainPrincipal().subtract(prinRepayAmt);
            BigDecimal intrAmt = dealPlan.getInterestAmount().subtract(dealPlan.getRepayInterest());
            if (CollectionUtils.isNotEmpty(saveBean.getUpdatePlanList())) {

                AmRepaymentPlanInfo upPlanInfo = saveBean.getUpdatePlanList().stream().filter(plan -> plan.getBillingTenor().equals(dealPlan.getBillingTenor())).findFirst().orElse(dealPlan);
                //如果还到本金，需要重新计算剩余利息，更新还款计划计划利息
                intrAmt = remainPrinAmt.multiply(BigDecimal.valueOf(DateUtils.getDaysBetween(bussinessDate, dealPlan.getPaymentDueDate()))).multiply(loanMainInfo.getActualDailyRate()).setScale(TransBizConstant.BIGDECIMAL_SCALE,
                        BigDecimal.ROUND_HALF_UP);
                upPlanInfo.setInterestAmount(intrAmt.add(loanAccountInfo.getInterestBalance()).add(repayInterestAmtBefore));
                upPlanInfo.setTotalAmount(dealPlan.getPrincipalAmount().add(upPlanInfo.getInterestAmount()));
                BeanMapping.copyUpdate(dealPlan, upPlanInfo);
            }
            //获取当期本金
            dealPlan.setIntrStartDate(bussinessDate);
            AmInterestAccrualInfo accuAcct = accountService.castInterestAccrualAccount(dealPlan, loanAccountInfo, loanMainInfo, remainPrinAmt, delqParamBean, intrAmt);
            if (null != accuAcct) {
                saveBean.getSaveAccuAcctList().add(accuAcct);
            }
        } else {
            //还款后剩余本金
            BigDecimal afterPayPrinBalAmt = prinBalAmt.subtract(prinRepayAmt);
            logger.info("还款后剩余本金afterPayPrinBalAmt:{}", afterPayPrinBalAmt);

            //当期结清处理
            PaymentSaveBean updatePlanSaveBean = new PaymentSaveBean();
            updatePlanSaveBean.setPaymentLogSaveList(saveBean.getPaymentLogSaveList());//09还款交易子账户
            updatePlanSaveBean.setAllAcctList(saveBean.getAllAcctList());
            Map<Integer, AmRepaymentPlanInfo> updatePlanMap = new HashMap<>();
            updatePlanMap.put(loanAccountInfo.getCastTenor(), saveBean.getPlanInfoMap().get(loanAccountInfo.getCastTenor()));
            updatePlanSaveBean.setPlanInfoMap(updatePlanMap);
            updatePlanSaveBean.setSavaPaymentLog(saveBean.getSavaPaymentLog());
            this.dealPlan(updatePlanSaveBean, bussinessDate);
            saveBean.getPlanInfoMap().put(loanAccountInfo.getCastTenor(), updatePlanSaveBean.getPlanInfoMap().get(loanAccountInfo.getCastTenor()));
            updatePlanSaveBean.getUpdatePlanList().forEach(
                    saveBean::addUpdatePlan
            );

            //当期不为最后一期
            if (afterPayPrinBalAmt.compareTo(BigDecimal.ZERO) > 0) {
                int tenor = loanMainInfo.getTotalTenor() - loanAccountInfo.getCastTenor();
                int castTenor = loanAccountInfo.getCastTenor() + 1;
                Date nextPaymentDate = DateUtils.addMonth(loanAccountInfo.getNextPaymentDate(), 1);
                //取消非当期还款计划
                this.cancelBillPayPlan(saveBean, loanAccountInfo.getCastTenor());
                //重新生成还款计划
                this.reBuildBillPrepayPlan(saveBean, loanMainInfo, loanAccountInfo, afterPayPrinBalAmt, bussinessDate, prProductTable, tenor, castTenor, nextPaymentDate);
                //重新抛帐
                this.reCastAcct(saveBean, loanMainInfo, loanAccountInfo, bussinessDate);

            }

        }
    }

    /**
     * 处理账单提前还还款之后的还款计划
     *
     * @param saveBean      保存本金利息罚息dto对象
     * @param loanMainInfo  贷款订单
     * @param bussinessDate 业务日期
     */
    private void dealBillPrepayPlan(PaymentSaveBean saveBean, AmLoanMainInfo loanMainInfo, AmLoanAccountInfo loanAccountInfo, Date bussinessDate,
                                    PrProductTable prProductTable, DebtBean debtBean, Date onDuePaymentDate, Date nextPaymentDate) throws TxnException {
        logger.info("处理账单提前还款后的还款计划");
        //还款金额
        BigDecimal repayAmt = saveBean.getPaymentLogSaveList().stream()
                .map(TlPaymentAllocationLog::getPostingAmount).reduce(BigDecimal.ZERO, BigDecimal::add);

        //没有任何还款
        if (repayAmt.compareTo(BigDecimal.ZERO) <= 0) {
            return;
        }

        //还款本金
        BigDecimal prinRepayAmt = saveBean.getPaymentLogSaveList().stream()
                .filter(log -> TransBizConstant.ACCOUNT_TYPE_06.equals(log.getConnectAccountType()))
                .map(TlPaymentAllocationLog::getPostingAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        //当期+下期欠款
        BigDecimal debtAmt = debtBean.getTotalAmount();
        //剩余未还本金
        BigDecimal prinBalAmt = loanAccountInfo.getRemainPrincipal();
        logger.info("还款金额repayAmt:{},当期+下期欠款debtAmt:{}", repayAmt, debtAmt);
        BigDecimal afterPayPrinBalAmt = prinBalAmt.subtract(prinRepayAmt);
        logger.info("还款后剩余本金afterPayPrinBalAmt:{}", afterPayPrinBalAmt);
        //  当期欠款<如果还款金额<当期+下期欠款
        if (repayAmt.compareTo(debtAmt) < 0) {

            //判断是否存在下期本金还款账户，如果存在，则说明该笔订单下一期账单已还到本金，需要重算还款计划，
            //如果未还到下期本金，假设下期未抛帐，则说明只需要结清当期，无需处理下期，否则结清当期，下期做部分还款处理
            //暂时不考虑逾期多期，还下期时，因为还款顺序为本，利，罚，复，而产生的本金还完，利/罚/复未还完的场景
            int nextTenor = saveBean.getPlanInfoMap().entrySet().stream()
                    .filter(entry -> entry.getValue().getPaymentDueDate().compareTo(nextPaymentDate) == 0)
                    .findFirst().get().getKey();
            boolean rebuild = saveBean.getPaymentLogSaveList().stream()
                    .filter(log -> TransBizConstant.ACCOUNT_TYPE_06.equals(log.getConnectAccountType()))
                    .anyMatch(log -> nextTenor == log.getBillingTenor());

            if (rebuild) {
                //如果存在当期，当期结清处理
                Map<Integer, AmRepaymentPlanInfo> updatePlanMap = saveBean.getPlanInfoMap().entrySet()
                        .stream().filter(entry -> entry.getValue().getPaymentDueDate().compareTo(onDuePaymentDate) <= 0)
                        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
                if (!updatePlanMap.isEmpty()) {
                    PaymentSaveBean updatePlanSaveBean = new PaymentSaveBean();
                    updatePlanSaveBean.setPaymentLogSaveList(saveBean.getPaymentLogSaveList());//09还款交易子账户
                    updatePlanSaveBean.setAllAcctList(saveBean.getAllAcctList());
                    updatePlanSaveBean.setPlanInfoMap(updatePlanMap);
                    updatePlanSaveBean.setSavaPaymentLog(saveBean.getSavaPaymentLog());
                    this.dealPlan(updatePlanSaveBean, bussinessDate);
                    updatePlanSaveBean.getPlanInfoMap().forEach(saveBean.getPlanInfoMap()::put);
                    updatePlanSaveBean.getUpdatePlanList().forEach(saveBean::addUpdatePlan);
                }

                //取消当期之后的还款计划
                int ondueTenor = nextTenor - 1;

                this.cancelBillPayPlan(saveBean, ondueTenor);
                int tenor = loanMainInfo.getTotalTenor() - ondueTenor;
                int castTenor = ondueTenor + 1;
                this.reBuildBillPrepayPlan(saveBean, loanMainInfo, loanAccountInfo, afterPayPrinBalAmt, bussinessDate, prProductTable, tenor, castTenor, nextPaymentDate);
            } else {

                BigDecimal repayInterestAmtBefore = saveBean.getPlanInfoMap().get(loanAccountInfo.getCastTenor()).getRepayInterest();

                this.dealPlan(saveBean, bussinessDate);

                //当期利息累计重新抛帐
                PrInterestTable interestTable = interestTableService.findInterestByInterestId(loanMainInfo.getInterestTableId(), loanMainInfo.getTenantId());
                if (TransBizConstant.START_DATE_FLAG_2.equals(interestTable.getStartDateFlag())) {
                    return;
                }

                DelqParamBean delqParamBean = delqService.buildDqlqAccountBean(loanMainInfo.getTenantId(), loanMainInfo.getDelqTableId());
                AmRepaymentPlanInfo dealPlan = saveBean.getPlanInfoMap().get(loanAccountInfo.getCastTenor());
                //剩余本金
                BigDecimal remainPrinAmt = loanAccountInfo.getRemainPrincipal().subtract(prinRepayAmt);
                BigDecimal intrAmt = dealPlan.getInterestAmount().subtract(dealPlan.getRepayInterest());
                if (CollectionUtils.isNotEmpty(saveBean.getUpdatePlanList())) {
                    AmRepaymentPlanInfo upPlanInfo = saveBean.getUpdatePlanList().stream().filter(plan -> plan.getBillingTenor().equals(dealPlan.getBillingTenor())).findFirst().orElse(dealPlan);
                    //如果还到本金，需要重新计算剩余利息，更新还款计划计划利息
                    intrAmt = remainPrinAmt.multiply(BigDecimal.valueOf(DateUtils.getDaysBetween(bussinessDate, dealPlan.getPaymentDueDate()))).multiply(loanMainInfo.getActualDailyRate()).setScale(TransBizConstant.BIGDECIMAL_SCALE,
                            BigDecimal.ROUND_HALF_UP);
                    upPlanInfo.setInterestAmount(intrAmt.add(loanAccountInfo.getInterestBalance()).add(repayInterestAmtBefore));
                    upPlanInfo.setTotalAmount(dealPlan.getPrincipalAmount().add(upPlanInfo.getInterestAmount()));
                    BeanMapping.copyUpdate(dealPlan, upPlanInfo);
                }
                //获取当期本金
                dealPlan.setIntrStartDate(bussinessDate);
                AmInterestAccrualInfo accuAcct = accountService.castInterestAccrualAccount(dealPlan, loanAccountInfo, loanMainInfo, remainPrinAmt, delqParamBean, intrAmt);
                if (null != accuAcct) {
                    saveBean.getSaveAccuAcctList().add(accuAcct);
                }
            }
        } else {
            //如果还款金额=当期+下期欠款
            //当期+下期结清处理
            PaymentSaveBean updatePlanSaveBean = new PaymentSaveBean();
            updatePlanSaveBean.setPaymentLogSaveList(saveBean.getPaymentLogSaveList());//09还款交易子账户
            updatePlanSaveBean.setAllAcctList(saveBean.getAllAcctList());
            Map<Integer, AmRepaymentPlanInfo> updatePlanMap = saveBean.getPlanInfoMap().entrySet()
                    .stream().filter(entry -> entry.getValue().getPaymentDueDate().compareTo(nextPaymentDate) <= 0)
                    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
            updatePlanSaveBean.setPlanInfoMap(updatePlanMap);
            updatePlanSaveBean.setSavaPaymentLog(saveBean.getSavaPaymentLog());
            this.dealPlan(updatePlanSaveBean, bussinessDate);
            updatePlanSaveBean.getPlanInfoMap().forEach(saveBean.getPlanInfoMap()::put);
            updatePlanSaveBean.getUpdatePlanList().forEach(saveBean::addUpdatePlan);

            //如果下一期不为最后一期
            if (afterPayPrinBalAmt.compareTo(BigDecimal.ZERO) > 0) {
                //取消下一期之后的还款计划
                int nextTenor = saveBean.getPlanInfoMap().entrySet().stream()
                        .filter(entry -> entry.getValue().getPaymentDueDate().compareTo(nextPaymentDate) == 0)
                        .findFirst().get().getKey();
                this.cancelBillPayPlan(saveBean, nextTenor);
                int tenor = loanMainInfo.getTotalTenor() - nextTenor;
                int castTenor = nextTenor + 1;
                Date paymentDate = DateUtils.addMonth(nextPaymentDate, 1);
                this.reBuildBillPrepayPlan(saveBean, loanMainInfo, loanAccountInfo, afterPayPrinBalAmt, bussinessDate, prProductTable, tenor, castTenor, paymentDate);
            }
        }
    }

    /**
     * 取消原还款计划
     *
     * @param saveBean 保存本金利息罚息dto对象
     */
    private void cancelPlan(PaymentSaveBean saveBean) {
        //所有需要处理的还款计划
        Map<Integer, AmRepaymentPlanInfo> planInfoMap = saveBean.getPlanInfoMap();
        //获取更新实体
        UnaryOperator<AmRepaymentPlanInfo> cancelPlan = plan -> {
            AmRepaymentPlanInfo updatePlan = new AmRepaymentPlanInfo();
            updatePlan.setId(plan.getId());
            updatePlan.setCustomerId(plan.getCustomerId());
            updatePlan.setTenantId(plan.getTenantId());
            updatePlan.setPlanStatus(TransBizConstant.LOAN_PLAN_STATUS_9);
            return updatePlan;
        };

        //需要更新还款计划
        planInfoMap.values().stream()
                .map(cancelPlan)//取消原还款计划
                .forEach(saveBean::addUpdatePlan);

    }

    /**
     * 取消原还款计划
     * 提前还当期，当期还款计划做结清处理，后面未抛的取消
     *
     * @param saveBean 保存本金利息罚息dto对象
     */
    private void cancelBillPayPlan(PaymentSaveBean saveBean, int cancelTenor) {
        //所有需要处理的还款计划
        Map<Integer, AmRepaymentPlanInfo> planInfoMap = saveBean.getPlanInfoMap();
        //获取更新实体
        UnaryOperator<AmRepaymentPlanInfo> cancelPlan = plan -> {
            AmRepaymentPlanInfo updatePlan = new AmRepaymentPlanInfo();
            updatePlan.setId(plan.getId());
            updatePlan.setCustomerId(plan.getCustomerId());
            updatePlan.setTenantId(plan.getTenantId());
            updatePlan.setPlanStatus(TransBizConstant.LOAN_PLAN_STATUS_9);
            return updatePlan;
        };

        //需要更新还款计划
        planInfoMap.values().stream()
                .filter(plan -> plan.getBillingTenor() > cancelTenor)
                .map(cancelPlan)//取消原还款计划
                .forEach(saveBean::addUpdatePlan);

    }


    /**
     * 重新生成还款计划
     *
     * @param saveBean           保存本金利息罚息dto对象
     * @param loanMainInfo       贷款信息
     * @param loanAccountInfo    贷款账户信息
     * @param afterPayPrinBalAmt 修改后的本金余额
     * @param bussinessDate      业务日期
     * @throws TxnException 自定义异常
     */
    private void reBuildPlan(PaymentSaveBean saveBean, AmLoanMainInfo loanMainInfo, AmLoanAccountInfo loanAccountInfo,
                             BigDecimal afterPayPrinBalAmt, PrProductTable prProductTable, Date bussinessDate) throws TxnException {
        logger.info("处理重新生成还款计划");
        Integer tenor = loanMainInfo.getTotalTenor() - loanAccountInfo.getCastTenor() + 1;

        //计算免息天数
        int discountDays = DateUtils.getDaysBetween(loanMainInfo.getPostingDate(), bussinessDate);
        //构建生成还款计划请求Bean，
        // 使用订单下个还款日作为重建还款计划的首期还款日，重建还款计划不需要计算首期还款计划
        if (TransBizConstant.LOAN_DAY_PAY_FLAG_1.equals(prProductTable.getLoanDayPayFlag()) && bussinessDate.compareTo(loanMainInfo.getPostingDate()) == 0
                // fix 账单分期订单，交易分期订单分期当晚不计利息
                && !Arrays.asList(TransBizConstant.LOAN_TYPE_3, TransBizConstant.LOAN_TYPE_7).contains(loanMainInfo.getLoanType())) {
            logger.info("当天借当天还且当天提前还款");
            bussinessDate = DateUtils.addDay(bussinessDate, 1);
            discountDays = 1;
        }

        LoanPlanParamBean paramBean = repaymentPlanService.buildLoanPlanParamBean(loanMainInfo, bussinessDate,
                afterPayPrinBalAmt, tenor, null, loanAccountInfo.getNextPaymentDate());
        PrInterestTable prInterestTable = interestTableService.findInterestByInterestId(loanMainInfo.getInterestTableId(), loanMainInfo.getTenantId());
        paramBean.setInterestTable(prInterestTable);
        paramBean.setPrProductTable(prProductTable);
        paramBean.setMinIntervalDays(prProductTable.getStatemntInterval());

        if (TransBizConstant.WAIVE_TYPE_2.equals(loanMainInfo.getWaiveType())) {
            String customerId = loanMainInfo.getCustomerId();
            String tenantId = loanMainInfo.getTenantId();
            Long custRightId = loanMainInfo.getCustRightId();
            CmCustomerRightInfo rightInfo = customerRightService.getRightInfoById(custRightId, customerId, tenantId);
            //提前还款免息天数需要减去之前已免息天数
            if (CustomerConstant.RIGHTS_TYPE_01.equals(rightInfo.getRightsType())) {
                rightInfo.setDiscountDays(Integer.max(rightInfo.getDiscountDays() - discountDays, 0));
            }
            paramBean.setRightInfo(rightInfo);
        } else if (TransBizConstant.WAIVE_TYPE_3.equals(loanMainInfo.getWaiveType()) &&
                TransBizConstant.PROMOTION_RATE_FIAG_2.equals(paramBean.getPrPromotionInfo().getRateFlag())) {
            //促销活动
            PrInterestTable oriInterestTable = interestTableService.findInterestByInterestId(loanMainInfo.getOriginalInterestTableId(), loanMainInfo.getTenantId());
            paramBean.setInterestTable(oriInterestTable);
        }
        //重新生成还款计划
        List<AmRepaymentPlanInfo> amRepaymentPlanInfoList = repaymentPlanService.buildRepaymentList(paramBean);

        amRepaymentPlanInfoList.forEach(
                plan -> {
                    plan.setBillingTenor(plan.getBillingTenor() + loanAccountInfo.getCastTenor() - 1);//将还款计划设置为正确的期数
                    saveBean.addSavePlan(plan);
                }
        );
    }


    /**
     * 账单提前还款重新生成还款计划
     *
     * @param saveBean
     * @param loanMainInfo
     * @param loanAccountInfo
     * @param afterPayPrinBalAmt
     * @param bussinessDate
     * @param prProductTable
     * @param tenor
     * @param castTenor
     * @param nextPaymentDate
     * @throws TxnBizException
     */
    private void reBuildBillPrepayPlan(PaymentSaveBean saveBean, AmLoanMainInfo loanMainInfo, AmLoanAccountInfo loanAccountInfo,
                                       BigDecimal afterPayPrinBalAmt, Date bussinessDate, PrProductTable prProductTable,
                                       int tenor, int castTenor, Date nextPaymentDate) throws TxnBizException {

        //提前还当期，如果当前业务日期与下一个还款日之间大于最小天数间隔，则新生成的还款计划的还款日为当期还款日
        Date onDuePaymentDate = DateUtils.addMonth(nextPaymentDate, -1);
        if (TransBizConstant.STATEMENT_DATE_TYPE_1.equals(prProductTable.getStatementDayType())) {
            if (onDuePaymentDate.compareTo(DateUtils.getDate(bussinessDate, prProductTable.getStatemntInterval())) > 0) {
                tenor = tenor - 1;
                castTenor = castTenor - 1;
                nextPaymentDate = onDuePaymentDate;
            }
        }

        loanAccountInfo.setNextPaymentDate(nextPaymentDate);
        loanAccountInfo.setCastTenor(castTenor);
        //构建生成还款计划请求Bean，
        // 使用订单下个还款日作为重建还款计划的首期还款日，重建还款计划不需要计算首期还款计划
        if (TransBizConstant.LOAN_DAY_PAY_FLAG_1.equals(prProductTable.getLoanDayPayFlag()) && bussinessDate.compareTo(loanMainInfo.getPostingDate()) == 0
                // fix 账单分期订单，交易分期订单分期当晚不计利息
                && !Arrays.asList(TransBizConstant.LOAN_TYPE_3, TransBizConstant.LOAN_TYPE_7).contains(loanMainInfo.getLoanType())) {
            logger.info("当天借当天还且当天提前还款");
            bussinessDate = DateUtils.addDay(bussinessDate, 1);
        }

        LoanPlanParamBean paramBean = repaymentPlanService.buildLoanPlanParamBean(loanMainInfo, bussinessDate,
                afterPayPrinBalAmt, tenor, null, nextPaymentDate);
        PrInterestTable prInterestTable = interestTableService.findInterestByInterestId(loanMainInfo.getInterestTableId(), loanMainInfo.getTenantId());
        paramBean.setInterestTable(prInterestTable);
        paramBean.setPrProductTable(prProductTable);
        paramBean.setMinIntervalDays(prProductTable.getStatemntInterval());
        //重新生成还款计划
        List<AmRepaymentPlanInfo> amRepaymentPlanInfoList = repaymentPlanService.buildRepaymentList(paramBean);

        amRepaymentPlanInfoList.forEach(
                plan -> {
                    AmRepaymentPlanInfo orgiPlanInfo = saveBean.getPlanInfoMap().values().stream()
                            .filter(orgiPlan -> orgiPlan.getPaymentDueDate().compareTo(plan.getPaymentDueDate()) == 0)
                            .findFirst().get();
                    plan.setBillingTenor(orgiPlanInfo.getBillingTenor());//将还款计划设置为正确的期数
                    saveBean.addSavePlan(plan);
                }
        );
    }

    /**
     * 处理订单信息
     *
     * @param saveBean        保存本金利息罚息dto对象
     * @param loanAccountInfo 贷款账户信息
     */
    private void dealOrder(PaymentSaveBean saveBean, AmLoanMainInfo amLoanMainInfo, AmLoanAccountInfo loanAccountInfo) throws TxnBizException {
        if (null == saveBean.getPlanInfoMap()) {
            return;
        }

        logger.info("更新订单开始,loanAccountInfo:{}", BeanUtils.toMap(loanAccountInfo));
        saveBean.addAmLoanMainInfo(amLoanMainInfo);
        //09还款交易账户
        List<TlPaymentAllocationLog> paymentLogList = saveBean.getPaymentLogSaveList();
        //已处理的还款计划
        List<AmRepaymentPlanInfo> updatePlanList = saveBean.getUpdatePlanList();
        //08还款交易账户
        TlPaymentAllocationLog totalPaymentLog = saveBean.getSavaPaymentLog();
        //延滞参数
        PrDelqTable prDelqTable = delqTableService.findByDelqId(amLoanMainInfo.getTenantId(), amLoanMainInfo.getDelqTableId());

        BigDecimal totalBalAmt = loanAccountInfo.getCurrBalance().subtract(totalPaymentLog.getPostingAmount());
        AmLoanAccountInfo updateLoan = new AmLoanAccountInfo();
        updateLoan.setId(loanAccountInfo.getId());
        updateLoan.setTenantId(loanAccountInfo.getTenantId());
        updateLoan.setCustomerId(loanAccountInfo.getCustomerId());
        updateLoan.setAccruedInterest(loanAccountInfo.getAccruedInterest());
        updateLoan.setAccruedPenalty(loanAccountInfo.getAccruedPenalty());
        updateLoan.setAccruedCompound(loanAccountInfo.getAccruedCompound());
        updateLoan.setNextPaymentDate(loanAccountInfo.getNextPaymentDate());
        updateLoan.setCastTenor(loanAccountInfo.getCastTenor());
        //只有回购才更新标识 代偿不更新
        if (totalPaymentLog.getPaymentType().equals(TransBizConstant.REPAYMENT_TYPE_CRT)) {
            updateLoan.setCompensatoryFlag(loanAccountInfo.getCompensatoryFlag());//代偿标识
        }
        updateLoan.setCurrBalance(totalBalAmt);
        //还款子交易账户按关联交易账户类型分组求本金，利息，罚息之和   key->账户类型，value->分组之和
        Map<String, BigDecimal> repayMap = paymentLogList.stream().collect(
                Collectors.groupingBy(TlPaymentAllocationLog::getConnectAccountType, CollectorsUtils.summingBigDecimal(TlPaymentAllocationLog::getPostingAmount))
        );
        logger.info("还款分组repayMap:{}", repayMap);

        BigDecimal prinAmt = Optional.ofNullable(repayMap.get(TransBizConstant.ACCOUNT_TYPE_06)).orElse(BigDecimal.ZERO);
        //更新所有已抛未还本金余额
        BigDecimal principalBalance = loanAccountInfo.getPrincipalBalance().subtract(prinAmt);
        updateLoan.setPrincipalBalance(principalBalance);
        //更新所有剩余本金
        BigDecimal remainPricipal = loanAccountInfo.getRemainPrincipal().subtract(prinAmt);
        updateLoan.setRemainPrincipal(remainPricipal);


        //更新利息余额
        BigDecimal intrAmt = Optional.ofNullable(repayMap.get(TransBizConstant.ACCOUNT_TYPE_04)).orElse(BigDecimal.ZERO);
        BigDecimal interestBalance = loanAccountInfo.getInterestBalance().subtract(intrAmt);
        updateLoan.setInterestBalance(interestBalance);


        //更新罚息余额
        BigDecimal pentAmt = Optional.ofNullable(repayMap.get(TransBizConstant.ACCOUNT_TYPE_11)).orElse(BigDecimal.ZERO);
        BigDecimal penaltyBalance = loanAccountInfo.getPenaltyBalance().subtract(pentAmt);
        updateLoan.setPenaltyBalance(penaltyBalance);


        //更新罚息复利余额
        BigDecimal compoundAmt = Optional.ofNullable(repayMap.get(TransBizConstant.ACCOUNT_TYPE_12)).orElse(BigDecimal.ZERO);
        BigDecimal compoundBalance = loanAccountInfo.getCompoundBalance().subtract(compoundAmt);
        updateLoan.setCompoundBalance(compoundBalance);


        //获取分期费用之和
        BigDecimal repayFee = paymentLogList.stream()
                .filter(log -> TransBizConstant.TXN_CODE_INST_PYMT_FEE.equals(log.getTxnCode()))
                .map(TlPaymentAllocationLog::getPostingAmount)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        //更新分期费用余额
        BigDecimal feeBalance = loanAccountInfo.getFeeBalance().subtract(repayFee);
        updateLoan.setFeeBalance(feeBalance);

        //更新总余额
        BigDecimal totalCurrBalance = loanAccountInfo.getCurrBalance().subtract(prinAmt).subtract(intrAmt).subtract(repayFee);
        updateLoan.setCurrBalance(totalCurrBalance);

        //如果不存在没有还完的还款计划，则订单已结清
        Predicate<AmRepaymentPlanInfo> notRepayPlan = planInfo -> TransBizConstant.LOAN_PLAN_STATUS_0.equals(planInfo.getPlanStatus())
                || TransBizConstant.LOAN_PLAN_STATUS_1.equals(planInfo.getPlanStatus())
                || TransBizConstant.LOAN_PLAN_STATUS_2.equals(planInfo.getPlanStatus());
        long tenorCount = saveBean.getPlanInfoMap().values().stream()
                .filter(notRepayPlan)
                .count();
        int maxTener = saveBean.getPlanInfoMap().values().stream()
                .map(AmRepaymentPlanInfo::getBillingTenor)
                .max(Integer::compareTo)
                .orElse(0);
        if (tenorCount <= 0) {
            if (TransBizConstant.REPAYMENT_TYPE_PRETERMINTION.equals(totalPaymentLog.getPaymentType())
                    || TransBizConstant.REPAYMENT_TYPE_PREPAY.equals(totalPaymentLog.getPaymentType())
                    || TransBizConstant.REPAYMENT_TYPE_CRT.equals(totalPaymentLog.getPaymentType())
                    || TransBizConstant.REPAYMENT_TYPE_PREPAY_CURR.equals(totalPaymentLog.getPaymentType())) {
                updateLoan.setAccountStatus(TransBizConstant.LOAN_STATUS_7);//提前结清
                updateLoan.setPayoffDate(totalPaymentLog.getPostingDate());
                updateLoan.setCastTenor(amLoanMainInfo.getTotalTenor());
                if (TransBizConstant.YES_FLAG.equals(prDelqTable.getClassifyReversible())) {
                    updateLoan.setLoanClassify(TransBizConstant.LOAN_CLASSIFY_0);
                }
            } else if (TransBizConstant.REPAYMENT_TYPE_STAGING.equals(totalPaymentLog.getPaymentType())) {
                updateLoan.setAccountStatus(TransBizConstant.LOAN_STATUS_6);//提前结清
                updateLoan.setPayoffDate(totalPaymentLog.getPostingDate());
                updateLoan.setCastTenor(amLoanMainInfo.getTotalTenor());
                if (TransBizConstant.YES_FLAG.equals(prDelqTable.getClassifyReversible())) {
                    updateLoan.setLoanClassify(TransBizConstant.LOAN_CLASSIFY_0);
                }
            } else {
                //正常还款，逾期还款如果还最后一期，订单更新未正常结清
                if (maxTener == amLoanMainInfo.getTotalTenor()) {
                    updateLoan.setAccountStatus(TransBizConstant.LOAN_STATUS_8);//正常结清
                    updateLoan.setPayoffDate(totalPaymentLog.getPostingDate());
                    if (TransBizConstant.YES_FLAG.equals(prDelqTable.getClassifyReversible())) {
                        updateLoan.setLoanClassify(TransBizConstant.LOAN_CLASSIFY_0);
                    }
                } else { //否则更新为正常
                    updateLoan.setAccountStatus(TransBizConstant.LOAN_STATUS_1);
                }
            }
        } else {
            //核销部分还款
            long overCount = saveBean.getPlanInfoMap().values().stream()
                    .filter(plan -> TransBizConstant.LOAN_PLAN_STATUS_2.equals(plan.getPlanStatus()))
                    .count();
            if (overCount <= 0) {
                updateLoan.setAccountStatus(TransBizConstant.LOAN_STATUS_1);
            }
        }

        //更新连续逾期天数
        if (StringUtils.isNotBlank(updateLoan.getAccountStatus()) && !TransBizConstant.LOAN_STATUS_2.equals(updateLoan.getAccountStatus())) {
            updateLoan.setContinueOverDueTenor(0);
        }

        //更新总代偿次数
        if (TransBizConstant.REPAYMENT_TYPE_CMPS.equals(totalPaymentLog.getPaymentType())) {
            if (loanAccountInfo.getTotalCompensatoryTenor() == null) {
                updateLoan.setTotalCompensatoryTenor(1);
            } else {
                int totalNumber = loanAccountInfo.getTotalCompensatoryTenor() + 1;
                updateLoan.setTotalCompensatoryTenor(totalNumber);//总代偿次数
            }
        }

        //获取最大延滞天数
        int delqMaxDays = saveBean.getPlanInfoMap().values().stream()
                .filter(plan -> TransBizConstant.LOAN_PLAN_STATUS_2.equals(plan.getPlanStatus()))
                .map(AmRepaymentPlanInfo::getDelqDays)
                .max(Integer::compareTo)
                .orElse(0);
        updateLoan.setDelqDays(delqMaxDays);


        //这次还完的期数
        Predicate<AmRepaymentPlanInfo> repayPlan = planInfo -> TransBizConstant.LOAN_PLAN_STATUS_7.equals(planInfo.getPlanStatus())
                || TransBizConstant.LOAN_PLAN_STATUS_8.equals(planInfo.getPlanStatus());
        long payedTenor = updatePlanList.stream().filter(repayPlan).count();
        int payedTenorNow = loanAccountInfo.getPayedTenor() + (int) payedTenor;
        updateLoan.setPayedTenor(payedTenorNow);
        logger.info("更新后的订单,loanAccountInfo:{}", BeanUtils.toMap(loanAccountInfo));
        saveBean.setUpdateLoan(updateLoan);
    }

    /**
     * 处理贴息
     *
     * @param saveBean
     * @param loanMainInfo
     * @param loanAccountInfo
     * @param prProductTable
     * @param bussinessDate
     * @throws TxnBizException
     */
    private void dealWaive(PaymentSaveBean saveBean, AmLoanMainInfo loanMainInfo, AmLoanAccountInfo loanAccountInfo, PrProductTable prProductTable, Date bussinessDate) throws TxnBizException {
        if (!TransBizConstant.WAIVE_TYPE_0.equals(loanMainInfo.getWaiveType())) {
            CustomerRes customerRes = customerService.getCustomerByChannelAndCustomerIdAndTensnId(loanMainInfo.getChannel(),
                    loanMainInfo.getCustomerId(), loanMainInfo.getTenantId());
            List<AmInterestAccrualInfo> amInterestAccrualInfos = extAmInterestAccrualInfoMapper.selectAccuAcct(loanMainInfo.getLoanId(), loanMainInfo.getCustomerId()
                    , loanMainInfo.getTenantId(), TransBizConstant.INTEREST_ACCRUAL_TYPE_70, loanAccountInfo.getCastTenor(), TransBizConstant.INTEREST_ACCRUAL_STATUS_1);

            boolean noAccuAcct = false;
            boolean noAcct = false;
            BigDecimal waiveAmt = BigDecimal.ZERO;

            //判断是否存在贴息累计
            if (CollectionUtils.isEmpty(amInterestAccrualInfos)) {
                noAccuAcct = true;
            } else {
                AmInterestAccrualInfo amInterestAccrualInfo = amInterestAccrualInfos.get(0);
                if (amInterestAccrualInfo.getAccrualTotalAccruInterest().compareTo(BigDecimal.ZERO) <= 0) {
                    noAccuAcct = true;
                } else {
                    waiveAmt = waiveAmt.add(amInterestAccrualInfo.getAccrualTotalAccruInterest());
                }
            }

            //判断是否存在贴息交易
            List<AmAccountMainInfo> amAccountMainInfos = extAmAccountMainInfoMapper.selectAccount(loanMainInfo.getLoanId(), loanMainInfo.getCustomerId()
                    , loanMainInfo.getTenantId(), Collections.singletonList(TransBizConstant.ACCOUNT_TYPE_17), loanAccountInfo.getCastTenor(), Collections.singletonList(TransBizConstant.ACCOUNT_STATUS_1));
            if (CollectionUtils.isEmpty(amAccountMainInfos)) {
                noAcct = true;
            } else {
                AmAccountMainInfo amAccountMainInfo = amAccountMainInfos.get(0);
                if (amAccountMainInfo.getCurrBalance().compareTo(BigDecimal.ZERO) <= 0) {
                    noAccuAcct = true;
                } else {
                    waiveAmt = waiveAmt.add(amAccountMainInfo.getCurrBalance());
                }
            }

            if (noAccuAcct && noAcct) {
                //对客已结清
                AmLoanAccountInfo updateLoan = saveBean.getUpdateLoan();
                if (Arrays.asList(TransBizConstant.LOAN_STATUS_6, TransBizConstant.LOAN_STATUS_7, TransBizConstant.LOAN_STATUS_8).contains(updateLoan.getAccountStatus())) {
                    //关闭贴息累计，贴息还款计划
                    this.dealPayOffWaivePlan(saveBean, loanMainInfo, bussinessDate);
                    this.dealPayOffWaiveAccuAcct(saveBean, amInterestAccrualInfos);
                }
                return;
            }

            TlWaivePaymentLog log = waivePaymentLogService.buildTlWaivePaymentLog(saveBean.getSavaPaymentLog().getPaymentType(), customerRes
                    , waiveAmt, bussinessDate, prProductTable);
            TlWaivePaymentMapping tlWaivePaymentMapping = waivePaymentLogService.biudTlWaivePaymentMapping(loanMainInfo.getCustomerId(), log.getSeqNo()
                    , loanMainInfo.getLoanId(), loanMainInfo.getTenantId());
            saveBean.getTlWaivePaymentLogList().add(log);
            saveBean.getTlWaivePaymentMappingList().add(tlWaivePaymentMapping);
        }
    }

    /**
     * 关闭剩余贴息还款计划
     *
     * @param saveBean
     * @param bussinessDate
     */
    private void dealPayOffWaivePlan(PaymentSaveBean saveBean, AmLoanMainInfo loanMainInfo, Date bussinessDate) {
        List<AmWaiveRepaymentPlanInfo> waiveRepaymentPlanInfoList = extAmWaiveRepaymentPlanInfoMapper.selectByLoanIdAndStatus(loanMainInfo.getLoanId(), Arrays.asList(TransBizConstant.LOAN_PLAN_STATUS_0, TransBizConstant.LOAN_PLAN_STATUS_1),
                loanMainInfo.getCustomerId(), loanMainInfo.getTenantId());

        if (CollectionUtils.isEmpty(waiveRepaymentPlanInfoList)) {
            return;
        }

        List<AmWaiveRepaymentPlanInfo> updateList = waiveRepaymentPlanInfoList.stream().map(
                plan -> {
                    AmWaiveRepaymentPlanInfo updatePlan = new AmWaiveRepaymentPlanInfo();
                    updatePlan.setId(plan.getId());
                    updatePlan.setCustomerId(plan.getCustomerId());
                    updatePlan.setTenantId(plan.getTenantId());
                    updatePlan.setBillingTenor(plan.getBillingTenor());
                    updatePlan.setPlanStatus(TransBizConstant.LOAN_PLAN_STATUS_7);
                    updatePlan.setPayoffDate(bussinessDate);
                    updatePlan.setTransactionDate(bussinessDate);
                    return updatePlan;
                }
        ).collect(Collectors.toList());
        saveBean.getUpdateWaivePlanList().addAll(updateList);
    }

    /**
     * 关闭贴息累计
     *
     * @param saveBean
     * @param accuAcctList
     */
    private void dealPayOffWaiveAccuAcct(PaymentSaveBean saveBean, List<AmInterestAccrualInfo> accuAcctList) {
        if (CollectionUtils.isEmpty(accuAcctList)) {
            return;
        }

        UncheckedConsumer<AmInterestAccrualInfo> closeWaiveAccu = accuAcct -> {
            //关闭原贴息累计
            AmInterestAccrualInfo updateAccuAcct = new AmInterestAccrualInfo();
            updateAccuAcct.setId(accuAcct.getId());
            updateAccuAcct.setLoanId(accuAcct.getLoanId());
            updateAccuAcct.setTenantId(accuAcct.getTenantId());
            updateAccuAcct.setCustomerId(accuAcct.getCustomerId());
            updateAccuAcct.setAccountStatus(TransBizConstant.ACCOUNT_STATUS_8);
            //保存生成的交易和需要更新的累计
            saveBean.addUpdateAccuAcct(updateAccuAcct);
        };
        accuAcctList.forEach(CollectorsUtils.tranToUncheck(closeWaiveAccu));

    }

    /**
     * 根据重新生成的还款计划，对当期本金，利息累计，分期费用重新抛帐
     *
     * @param saveBean        保存本金利息罚息dto对象
     * @param loanMainInfo    贷款信息
     * @param loanAccountInfo 贷款账户信息
     * @param bussinessDate   业务日期
     * @throws TxnException 自定义异常
     */
    private void reCastAcct(PaymentSaveBean saveBean, AmLoanMainInfo loanMainInfo, AmLoanAccountInfo loanAccountInfo, Date bussinessDate) throws TxnException {
        //判断是否有新生成的还款计划  有 提前部分还款  没有 提前结清
        if (CollectionUtils.isEmpty(saveBean.getSavePlanList())) {//没有直接返回 不做任何处理
            return;
        }

        logger.info("重新抛帐,loanMainInfo:{},loanAccountInfo:{}", BeanUtils.toMap(loanMainInfo), BeanUtils.toMap(loanAccountInfo));
        if (!TransBizConstant.LOAN_STATUS_1.equals(loanAccountInfo.getAccountStatus())) {
            logger.info("订单已结清");
            return;
        }


        if (bussinessDate.equals(loanAccountInfo.getNextPaymentDate())) {
            logger.info("还款日当天不抛帐");
            return;
        }

        //提前还款场景特殊，此时抛帐需要生成当期的交易账户，所以在这里特殊处理
        loanAccountInfo.setCastTenor(loanAccountInfo.getCastTenor() - 1);

        //本金,分期费用，利息累计抛帐
        List<AmRepaymentPlanInfo> planInfoList = saveBean.getSavePlanList();
        CastAccountBean castAccountBean = accountService.castTxnAccount(loanMainInfo, loanAccountInfo, planInfoList, bussinessDate, false);
        saveBean.addSaveAcctList(castAccountBean.getAccountMainInfoList());
        saveBean.addTransactionInfoList(castAccountBean.getTransactionInfoList());
        saveBean.addSaveAccuAcctList(castAccountBean.getInterestAccrualInfoList());
        saveBean.addSaveGlList(castAccountBean.getGlInterfaceInfoList());
    }

    /**
     * 更新客户额度
     *
     * @param saveBean     保存本金利息罚息dto对象
     * @param loanMainInfo 贷款信息
     */
    private void updateCustLmt(PaymentSaveBean saveBean, AmLoanMainInfo loanMainInfo) throws TxnBizException {
        String customerId = loanMainInfo.getCustomerId();
        String tenantId = loanMainInfo.getTenantId();
        String channel = loanMainInfo.getChannel();
        String orgId = loanMainInfo.getOrganizationId();
        String productId = loanMainInfo.getProductId();

        List<TlPaymentAllocationLog> paymentLogSaveList = saveBean.getPaymentLogSaveList();
        //获取还款本金
        BigDecimal prinAmt = paymentLogSaveList.stream()
                .filter(log -> TransBizConstant.ACCOUNT_TYPE_06.equals(log.getConnectAccountType()))//筛选本金还款
                .map(TlPaymentAllocationLog::getPostingAmount)
                .reduce(BigDecimal.ZERO, BigDecimal::add);//累加还款金额
        //更新额度
        customerLimitInfoService.modifyCustomerLimitByPayment(tenantId, customerId, orgId, channel, productId, prinAmt);
    }

    /**
     * 更新合作方额度
     *
     * @param saveBean     保存本金利息罚息dto对象
     * @param loanMainInfo 贷款信息
     */
    private void updatePartnerLimit(PaymentSaveBean saveBean, AmLoanMainInfo loanMainInfo, String seqNo) throws TxnBizException {
        String tenantId = loanMainInfo.getTenantId();
        String orgId = loanMainInfo.getOrganizationId();
        String productId = loanMainInfo.getProductId();

        List<TlPaymentAllocationLog> paymentLogSaveList = saveBean.getPaymentLogSaveList();
        //获取还款本金
        BigDecimal prinAmt = paymentLogSaveList.stream()
                .filter(log -> TransBizConstant.ACCOUNT_TYPE_06.equals(log.getConnectAccountType()))//筛选本金还款
                .map(TlPaymentAllocationLog::getPostingAmount)
                .reduce(BigDecimal.ZERO, BigDecimal::add);//累加还款金额
        //恢复额度
        partnerLimitRecoveryService.partnerLimitRecovery(productId, orgId, tenantId, prinAmt, seqNo);
    }

    /**
     * 更新客户额度
     *
     * @param saveBeanList     保存本金利息罚息dto list
     * @param loanMainInfoList 贷款信息list
     */
    private void updateCustLmt(List<PaymentSaveBean> saveBeanList, List<AmLoanMainInfo> loanMainInfoList) throws TxnBizException {
        PaymentSaveBean saveBean = new PaymentSaveBean();
        saveBeanList.stream()
                .map(PaymentSaveBean::getPaymentLogSaveList) //获取每一个List<TlPaymentAllocationLog>
                .filter(CollectionUtils::isNotEmpty)
                .forEach(saveBean::addPaymentLogList);
        //一笔更新额度
        this.updateCustLmt(saveBean, loanMainInfoList.get(0));
    }

    private void updatePartnerLimit(List<PaymentSaveBean> saveBeanList, List<AmLoanMainInfo> loanMainInfoList, String seqNo) throws TxnBizException {
        PaymentSaveBean saveBean = new PaymentSaveBean();
        saveBeanList.stream()
                .map(PaymentSaveBean::getPaymentLogSaveList) //获取每一个List<TlPaymentAllocationLog>
                .filter(CollectionUtils::isNotEmpty)
                .forEach(saveBean::addPaymentLogList);
        //一笔更新额度
        this.updatePartnerLimit(saveBean, loanMainInfoList.get(0), seqNo);
    }

    /**
     * 下期还款计划如果未抛，则将本金账户抛帐,否则不处理
     *
     * @param saveBean
     * @param nextPaymentDate
     */
    private void castNextStatmentPrinAcct(PaymentSaveBean saveBean, AmLoanMainInfo loanMainInfo, AmLoanAccountInfo loanAccountInfo, DelqParamBean delqParamBean,
                                          Date nextPaymentDate, Date bussinessDate) {
        Map<Integer, AmRepaymentPlanInfo> planInfoMap = saveBean.getPlanInfoAllMap().get(loanMainInfo.getLoanId());
        //循环未抛帐的还款计划，生成对应的本金交易账户
        UncheckedBiConsumer<Integer, AmRepaymentPlanInfo> castPrinAcct = (tenor, plan) -> {
            if (plan.getPaymentDueDate().compareTo(nextPaymentDate) == 0
                    && TransBizConstant.LOAN_PLAN_STATUS_0.equals(plan.getPlanStatus())) {
                CastAccountTransactionBean intrCastAccrt = accountService.castPrincipalAccount(loanMainInfo, loanAccountInfo, plan, delqParamBean, bussinessDate);
                saveBean.addSaveAcct(intrCastAccrt.getAmAccountMainInfo());
                saveBean.addTransactionInfo(intrCastAccrt.getTlTransactionInfo());
                saveBean.addSaveGlList(intrCastAccrt.getTlGlInterfaceInfoList());
            }
        };
        planInfoMap.forEach(CollectorsUtils.tranToUncheck(castPrinAcct));
    }

    /**
     * 保存所有还款信息
     *
     * @param saveBean 保存本金利息罚息dto对象
     */
    @Transactional
    @Override
    public void saveAllAccts(PaymentSaveBean saveBean) {
        TlPaymentAllocationLog savaPaymentLog = saveBean.getSavaPaymentLog();//需要保存的还款总交易账户
        //需要保存的还款子交易账户
        List<TlPaymentAllocationLog> paymentLogSaveList = saveBean.getPaymentLogSaveList().stream()
                .filter(log -> log.getPostingAmount().compareTo(BigDecimal.ZERO) > 0)
                .collect(Collectors.toList());
        paymentLogSaveList.add(savaPaymentLog);
        extTlPaymentAllocationLogMapper.batchInsertSelective(paymentLogSaveList);

        List<AmInterestAccrualInfo> saveAccuAcctList = saveBean.getSaveAccuAcctList();//需要保存的累计账户
        if (CollectionUtils.isNotEmpty(saveAccuAcctList)) {
            extAmInterestAccrualInfoMapper.batchInsertInterestAccrualInfo(saveAccuAcctList);
        }

        List<AmInterestAccrualInfo> updateAccuAcctList = saveBean.getUpdateAccuAcctList();//需要更新的累计账户
        if (CollectionUtils.isNotEmpty(updateAccuAcctList)) {
            extAmInterestAccrualInfoMapper.batchUpdateInterestAccrualInfo(updateAccuAcctList);
        }

        List<AmAccountMainInfo> saveAcctList = saveBean.getSaveAcctList();//保存的交易账户
        if (CollectionUtils.isNotEmpty(saveAcctList)) {
            extAmAccountMainInfoMapper.batchInsertAccount(saveAcctList);
        }
        List<AmAccountMainInfo> updateAcctList = saveBean.getUpdateAcctList();//需要更新交易账户
        if (CollectionUtils.isNotEmpty(updateAcctList)) {
            extAmAccountMainInfoMapper.batchUpdateAccount(updateAcctList);
        }

        List<TlGlInterfaceInfo> glSaveList = saveBean.getSaveGlList();//需要保存的gl流水
        if (CollectionUtils.isNotEmpty(glSaveList)) {
            extTlGlInterfaceInfoMapper.batchInsertSelective(glSaveList);
        }

        List<AmRepaymentPlanInfo> savePlanList = saveBean.getSavePlanList();//需要保存的还款计划
        if (CollectionUtils.isNotEmpty(savePlanList)) {
            extAmRepaymentPlanInfoMapper.insertAmRepaymentPlanInfoList(savePlanList);
        }

        List<AmRepaymentPlanInfo> updatePlanList = saveBean.getUpdatePlanList();//需要更新的还款计划
        if (CollectionUtils.isNotEmpty(updatePlanList)) {
            int size = updatePlanList.size();
            int range = 20;
            int start = 0;
            // 修改次数
            int count = (int) Math.ceil((double) size / range);
            for (int i = 1; i <= count; i++) {
                List<AmRepaymentPlanInfo> amRepaymentPlanInfoList = updatePlanList.subList(start, i == count ? size : i * range);
                start = i * range;
                extAmRepaymentPlanInfoMapper.batchUpdatePlan(amRepaymentPlanInfoList);
            }
        }

        List<AmDelinquencyInfo> updateDelqList = saveBean.getUpdateDelqList();////需要更新的延滞交易账户
        if (CollectionUtils.isNotEmpty(updateDelqList)) {
            extAmDelinquencyInfoMapper.batchUpdateAccount(updateDelqList);
        }

        AmLoanAccountInfo updateLoan = saveBean.getUpdateLoan();
        if (null != updateLoan) {//需要更新的订
            extAmLoanAccountInfoMapper.updateByPrimaryKey(updateLoan);
        }

        List<AmLoanAccountInfo> updateLoanList = saveBean.getUpdateLoanList();//需要更新的订单
        if (CollectionUtils.isNotEmpty(updateLoanList)) {
            extAmLoanAccountInfoMapper.batchUpdateLoanAccount(updateLoanList);
        }

        List<TlTransactionInfo> transactionInfos = saveBean.getTransactionInfoList();
        if (CollectionUtils.isNotEmpty(transactionInfos)) {
            extTlTransactionInfoMapper.batchInsert(transactionInfos);
        }

        List<BtAccountingTransferList> transferLists = saveBean.getTransferLists();
        if (CollectionUtils.isNotEmpty(transferLists)) {
            extBtAccountingTransferListMapper.batchInsert(transferLists);
        }

        List<TlWaivePaymentLog> tlWaivePaymentLogList = saveBean.getTlWaivePaymentLogList();
        if (CollectionUtils.isNotEmpty(tlWaivePaymentLogList)) {
            extTlWaivePaymentLogMapper.batchInsert(tlWaivePaymentLogList);
        }

        List<TlWaivePaymentMapping> tlWaivePaymentMappingList = saveBean.getTlWaivePaymentMappingList();
        if (CollectionUtils.isNotEmpty(tlWaivePaymentMappingList)) {
            extTlWaivePaymentMappingMapper.batchInsert(tlWaivePaymentMappingList);
        }

        List<AmWaiveRepaymentPlanInfo> amWaiveRepaymentPlanInfoList = saveBean.getUpdateWaivePlanList();
        if (CollectionUtils.isNotEmpty(amWaiveRepaymentPlanInfoList)) {
            extAmWaiveRepaymentPlanInfoMapper.batchUpdatePlan(amWaiveRepaymentPlanInfoList);
        }

        List<TlLoanClassifyChange> tlLoanClassifyChangeList = saveBean.getSaveClassifyList();
        if (CollectionUtils.isNotEmpty(tlLoanClassifyChangeList)) {
            extTlLoanClassifyChangeMapper.batchInsert(tlLoanClassifyChangeList);
        }


    }


    /**
     * 处理最终保存对象
     *
     * @param saveBeanList
     * @param tlTransactionInfo
     * @return
     */
    private PaymentSaveBean dealAllAccts(List<PaymentSaveBean> saveBeanList, TlTransactionInfo tlTransactionInfo) {
        PaymentSaveBean saveBean = saveBeanList.stream().reduce(new PaymentSaveBean(), PaymentSaveBean::add);
        List<TlTransactionInfo> transactionInfos = saveBean.getTransactionInfoList();
        transactionInfos.add(tlTransactionInfo);
        return saveBean;
    }
}
